Introduced FastAPI and Postgres support (#173)
* added poc * added check for sqlite db * added events test * draft docker-compose-test.yml * improved docker-compose * added support for postgres migrations * add documentation * added some qol to migrations * added migration generation script * removed settings.toml * waiting for postgress in script * commented script * added sample web config * fixed tests * mock memory db * reviewed PR
This commit is contained in:
parent
63a30bb483
commit
44340fde8f
|
@ -189,3 +189,4 @@ api_documentation/source/*
|
||||||
!api_documentation/source/conf.py
|
!api_documentation/source/conf.py
|
||||||
!api_documentation/source/index.rst
|
!api_documentation/source/index.rst
|
||||||
!api_documentation/source/_static/
|
!api_documentation/source/_static/
|
||||||
|
./settings.toml
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
FROM python:3.10-alpine3.16
|
||||||
|
|
||||||
|
ENV ENV_FOR_DYNACONF=${ENV_FOR_DYNACONF} \
|
||||||
|
PYTHONFAULTHANDLER=1 \
|
||||||
|
PYTHONUNBUFFERED=1 \
|
||||||
|
PYTHONHASHSEED=random \
|
||||||
|
PIP_NO_CACHE_DIR=off \
|
||||||
|
PIP_DISABLE_PIP_VERSION_CHECK=on \
|
||||||
|
PIP_DEFAULT_TIMEOUT=100 \
|
||||||
|
POETRY_VERSION=1.0.0
|
||||||
|
|
||||||
|
# System deps:
|
||||||
|
RUN pip install "poetry==$POETRY_VERSION"
|
||||||
|
|
||||||
|
# Copy only requirements to cache them in docker layer
|
||||||
|
WORKDIR /app
|
||||||
|
COPY poetry.lock pyproject.toml /app/
|
||||||
|
|
||||||
|
# Project initialization:
|
||||||
|
RUN poetry config virtualenvs.create false \
|
||||||
|
&& poetry install $(test "$ENV_FOR_DYNACONF" == production && echo "--no-dev") --no-interaction --no-ansi
|
||||||
|
|
||||||
|
# Creating folders, and files for a project:
|
||||||
|
COPY . /app
|
|
@ -25,3 +25,25 @@ To run the test suite, run `scripts/run_pipeline_tests.sh` from the root of the
|
||||||
|
|
||||||
At the moment no integration test is present and they are executed manually. Reach out to us if you want to
|
At the moment no integration test is present and they are executed manually. Reach out to us if you want to
|
||||||
access the testing environment or you want to help automate the integration tests.
|
access the testing environment or you want to help automate the integration tests.
|
||||||
|
|
||||||
|
### How to handle migrations
|
||||||
|
|
||||||
|
Changes to the data model need to be handled through migrations. We use aerich to manage the migration files.
|
||||||
|
Both our CLI and our web service are configured in such a way that migrations are run transparently when the package is
|
||||||
|
updated. If you want to test that the update doesn't corrupt your data, we suggest trying the update in a test database.
|
||||||
|
|
||||||
|
To create a new migration file, use aerich CLI. It will take care of generating the file. If further code is necessary,
|
||||||
|
add it to the new migration file.
|
||||||
|
|
||||||
|
Since we support two database (sqlite and postgres) that have slightly different dialects and since aerich doesn't
|
||||||
|
really support this scenario, it is necessary to generate migrations separately and place the migrations files in the
|
||||||
|
respective folders.
|
||||||
|
|
||||||
|
Aerich picks up the migrations according to the scheme of the db in the configuration.
|
||||||
|
|
||||||
|
Currently the consistency of the migrations for the different databases is not tested so please pay extra care when
|
||||||
|
committing a change and request special review.
|
||||||
|
|
||||||
|
Aerich configuration is specified in the pyproject.toml file. Since it doesn't support multiple databases, we have two
|
||||||
|
configuration files that allow to run aerich on different databases if you enter their respective migration folders.
|
||||||
|
You can find them in mobilizon_reshare/migrations.
|
|
@ -0,0 +1,12 @@
|
||||||
|
version: "3.7"
|
||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: postgres:13
|
||||||
|
env_file:
|
||||||
|
- ./.env
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "pg_isready", "-U", "mobilizon_reshare"]
|
||||||
|
interval: 5s
|
||||||
|
retries: 5
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
|
@ -0,0 +1,34 @@
|
||||||
|
version: "3.7"
|
||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: postgres:13
|
||||||
|
env_file:
|
||||||
|
- ./.env
|
||||||
|
volumes:
|
||||||
|
- postgres-db-volume:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "pg_isready", "-U", "mobilizon_reshare"]
|
||||||
|
interval: 5s
|
||||||
|
retries: 5
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
web:
|
||||||
|
build: .
|
||||||
|
command: poetry run mobilizon-reshare web
|
||||||
|
#command: sh
|
||||||
|
environment:
|
||||||
|
SECRETS_FOR_DYNACONF: /app/.secrets.toml
|
||||||
|
SETTINGS_FILE_FOR_DYNACONF: /app/settings.toml
|
||||||
|
ENV_FOR_DYNACONF: development
|
||||||
|
volumes:
|
||||||
|
- ./sample_settings/docker_web/.sample_secrets.toml:/app/.secrets.toml
|
||||||
|
- ./sample_settings/docker_web/settings.toml:/app/settings.toml
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
- /etc/timezone:/etc/timezone:ro
|
||||||
|
- postgres-db-volume:/var/lib/postgresql
|
||||||
|
- ./:/app
|
||||||
|
ports:
|
||||||
|
- 8000:8000
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres-db-volume:
|
|
@ -1,4 +0,0 @@
|
||||||
[aerich]
|
|
||||||
tortoise_orm = storage.db.TORTOISE_ORM
|
|
||||||
location = ./migrations
|
|
||||||
src_folder = ./.
|
|
|
@ -1,14 +1,11 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
|
||||||
from logging.config import dictConfig
|
|
||||||
from pathlib import Path
|
|
||||||
import sys
|
import sys
|
||||||
|
import traceback
|
||||||
|
|
||||||
from mobilizon_reshare.config.command import CommandConfig
|
from mobilizon_reshare.config.command import CommandConfig
|
||||||
from mobilizon_reshare.config.config import get_settings
|
from mobilizon_reshare.storage.db import tear_down, init
|
||||||
from mobilizon_reshare.storage.db import tear_down, MoReDB
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -17,14 +14,6 @@ async def graceful_exit():
|
||||||
await tear_down()
|
await tear_down()
|
||||||
|
|
||||||
|
|
||||||
async def init():
|
|
||||||
settings = get_settings()
|
|
||||||
dictConfig(settings["logging"])
|
|
||||||
db_path = Path(settings.db_path)
|
|
||||||
db = MoReDB(db_path)
|
|
||||||
await db.setup()
|
|
||||||
|
|
||||||
|
|
||||||
async def _safe_execution(function):
|
async def _safe_execution(function):
|
||||||
await init()
|
await init()
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,25 @@
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
import uvicorn
|
||||||
from click import pass_context
|
from click import pass_context
|
||||||
|
|
||||||
from mobilizon_reshare.config.command import CommandConfig
|
|
||||||
from mobilizon_reshare.cli import safe_execution
|
from mobilizon_reshare.cli import safe_execution
|
||||||
from mobilizon_reshare.cli.commands.format.format import format_event
|
from mobilizon_reshare.cli.commands.format.format import format_event
|
||||||
from mobilizon_reshare.cli.commands.list.list_event import list_events
|
from mobilizon_reshare.cli.commands.list.list_event import list_events
|
||||||
from mobilizon_reshare.cli.commands.list.list_publication import list_publications
|
from mobilizon_reshare.cli.commands.list.list_publication import list_publications
|
||||||
from mobilizon_reshare.cli.commands.recap.main import recap_command as recap_main
|
|
||||||
from mobilizon_reshare.cli.commands.start.main import start_command as start_main
|
|
||||||
from mobilizon_reshare.cli.commands.pull.main import pull_command as pull_main
|
|
||||||
from mobilizon_reshare.cli.commands.publish.main import publish_command as publish_main
|
from mobilizon_reshare.cli.commands.publish.main import publish_command as publish_main
|
||||||
from mobilizon_reshare.config.config import current_version, get_settings
|
from mobilizon_reshare.cli.commands.pull.main import pull_command as pull_main
|
||||||
from mobilizon_reshare.config.publishers import publisher_names
|
from mobilizon_reshare.cli.commands.recap.main import recap_command as recap_main
|
||||||
from mobilizon_reshare.event.event import EventPublicationStatus
|
|
||||||
from mobilizon_reshare.cli.commands.retry.main import (
|
from mobilizon_reshare.cli.commands.retry.main import (
|
||||||
retry_event_command,
|
retry_event_command,
|
||||||
retry_publication_command,
|
retry_publication_command,
|
||||||
)
|
)
|
||||||
|
from mobilizon_reshare.cli.commands.start.main import start_command as start_main
|
||||||
|
from mobilizon_reshare.config.command import CommandConfig
|
||||||
|
from mobilizon_reshare.config.config import current_version, get_settings
|
||||||
|
from mobilizon_reshare.config.publishers import publisher_names
|
||||||
|
from mobilizon_reshare.event.event import EventPublicationStatus
|
||||||
from mobilizon_reshare.models.publication import PublicationStatus
|
from mobilizon_reshare.models.publication import PublicationStatus
|
||||||
from mobilizon_reshare.publishers import get_active_publishers
|
from mobilizon_reshare.publishers import get_active_publishers
|
||||||
|
|
||||||
|
@ -249,5 +250,12 @@ def publication_retry(publication_id):
|
||||||
safe_execution(functools.partial(retry_publication_command, publication_id),)
|
safe_execution(functools.partial(retry_publication_command, publication_id),)
|
||||||
|
|
||||||
|
|
||||||
|
@mobilizon_reshare.command("web")
|
||||||
|
def web():
|
||||||
|
uvicorn.run(
|
||||||
|
"mobilizon_reshare.web.backend.main:app", host="0.0.0.0", port=8000, reload=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
mobilizon_reshare(obj={})
|
mobilizon_reshare(obj={})
|
||||||
|
|
|
@ -20,7 +20,7 @@ base_validators = [
|
||||||
# url of the main Mobilizon instance to download events from
|
# url of the main Mobilizon instance to download events from
|
||||||
Validator("source.mobilizon.url", must_exist=True, is_type_of=str),
|
Validator("source.mobilizon.url", must_exist=True, is_type_of=str),
|
||||||
Validator("source.mobilizon.group", must_exist=True, is_type_of=str),
|
Validator("source.mobilizon.group", must_exist=True, is_type_of=str),
|
||||||
Validator("db_path", must_exist=True, is_type_of=str),
|
Validator("db_url", must_exist=True, is_type_of=str),
|
||||||
Validator("locale", must_exist=True, is_type_of=str, default="en-us"),
|
Validator("locale", must_exist=True, is_type_of=str, default="en-us"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -130,3 +130,7 @@ class CustomConfig:
|
||||||
|
|
||||||
def get_settings():
|
def get_settings():
|
||||||
return CustomConfig.get_instance().settings
|
return CustomConfig.get_instance().settings
|
||||||
|
|
||||||
|
|
||||||
|
def get_settings_without_validation():
|
||||||
|
return build_settings()
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
-- upgrade --
|
||||||
|
CREATE TABLE IF NOT EXISTS "event" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"description" TEXT,
|
||||||
|
"mobilizon_id" UUID NOT NULL,
|
||||||
|
"mobilizon_link" TEXT NOT NULL,
|
||||||
|
"thumbnail_link" TEXT,
|
||||||
|
"location" TEXT,
|
||||||
|
"begin_datetime" TIMESTAMPTZ NOT NULL,
|
||||||
|
"end_datetime" TIMESTAMPTZ NOT NULL,
|
||||||
|
"last_update_time" TIMESTAMPTZ NOT NULL
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS "publisher" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"name" VARCHAR(256) NOT NULL,
|
||||||
|
"account_ref" TEXT
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS "publication" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"status" SMALLINT NOT NULL,
|
||||||
|
"timestamp" TIMESTAMPTZ NOT NULL,
|
||||||
|
"reason" TEXT,
|
||||||
|
"event_id" UUID NOT NULL REFERENCES "event" ("id") ON DELETE CASCADE,
|
||||||
|
"publisher_id" UUID NOT NULL REFERENCES "publisher" ("id") ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
COMMENT ON COLUMN "publication"."status" IS 'FAILED: 0\nCOMPLETED: 1';
|
||||||
|
CREATE TABLE IF NOT EXISTS "notification" (
|
||||||
|
"id" UUID NOT NULL PRIMARY KEY,
|
||||||
|
"status" SMALLINT NOT NULL,
|
||||||
|
"message" TEXT NOT NULL,
|
||||||
|
"publication_id" UUID REFERENCES "publication" ("id") ON DELETE CASCADE,
|
||||||
|
"target_id" UUID REFERENCES "publisher" ("id") ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
COMMENT ON COLUMN "notification"."status" IS 'WAITING: 1\nFAILED: 2\nPARTIAL: 3\nCOMPLETED: 4';
|
||||||
|
CREATE TABLE IF NOT EXISTS "aerich" (
|
||||||
|
"id" SERIAL NOT NULL PRIMARY KEY,
|
||||||
|
"version" VARCHAR(255) NOT NULL,
|
||||||
|
"app" VARCHAR(100) NOT NULL,
|
||||||
|
"content" JSONB NOT NULL
|
||||||
|
);
|
|
@ -0,0 +1,4 @@
|
||||||
|
[tool.aerich]
|
||||||
|
tortoise_orm = "mobilizon_reshare.storage.db.TORTOISE_ORM"
|
||||||
|
location = "./"
|
||||||
|
src_folder = "./."
|
|
@ -0,0 +1,4 @@
|
||||||
|
[tool.aerich]
|
||||||
|
tortoise_orm = "mobilizon_reshare.storage.db.TORTOISE_ORM"
|
||||||
|
location = "."
|
||||||
|
src_folder = "./."
|
|
@ -1,7 +1,6 @@
|
||||||
[default]
|
[default]
|
||||||
local_state_dir = "/var/mobilizon_reshare"
|
local_state_dir = "/var/mobilizon_reshare"
|
||||||
db_name = "events.db"
|
db_url = "sqlite:///var/mobilizon_reshare/events.db"
|
||||||
db_path = "@format {this.local_state_dir}/{this.db_name}"
|
|
||||||
locale= "en-us"
|
locale= "en-us"
|
||||||
|
|
||||||
[default.source.mobilizon]
|
[default.source.mobilizon]
|
||||||
|
|
|
@ -1,32 +1,30 @@
|
||||||
import logging
|
import logging
|
||||||
|
from logging.config import dictConfig
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
from tortoise import Tortoise
|
import urllib3.util
|
||||||
from aerich import Command
|
from aerich import Command
|
||||||
|
from tortoise import Tortoise
|
||||||
|
|
||||||
|
from mobilizon_reshare.config.config import (
|
||||||
|
get_settings,
|
||||||
|
get_settings_without_validation,
|
||||||
|
)
|
||||||
from mobilizon_reshare.config.publishers import publisher_names
|
from mobilizon_reshare.config.publishers import publisher_names
|
||||||
from mobilizon_reshare.storage.query.write import update_publishers
|
from mobilizon_reshare.storage.query.write import update_publishers
|
||||||
|
|
||||||
from mobilizon_reshare.config.config import get_settings
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_db_url():
|
def get_db_url() -> urllib3.util.Url:
|
||||||
"""gets db url from settings
|
return urllib3.util.parse_url(get_settings_without_validation().db_url)
|
||||||
|
|
||||||
Returns:
|
|
||||||
str : db url
|
|
||||||
"""
|
|
||||||
settings = get_settings()
|
|
||||||
db_path = Path(settings.db_path)
|
|
||||||
db_url = f"sqlite:///{db_path}"
|
|
||||||
return db_url
|
|
||||||
|
|
||||||
|
|
||||||
def get_tortoise_orm():
|
def get_tortoise_orm():
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"connections": {"default": get_db_url()},
|
"connections": {"default": get_db_url().url},
|
||||||
"apps": {
|
"apps": {
|
||||||
"models": {
|
"models": {
|
||||||
"models": [
|
"models": [
|
||||||
|
@ -48,39 +46,57 @@ TORTOISE_ORM = get_tortoise_orm()
|
||||||
|
|
||||||
|
|
||||||
class MoReDB:
|
class MoReDB:
|
||||||
def __init__(self, path: Path):
|
def get_migration_location(self):
|
||||||
self.path = path
|
scheme = get_db_url().scheme
|
||||||
# TODO: Check if DB is openable/"queriable"
|
return pkg_resources.resource_filename(
|
||||||
self.is_init = self.path.exists() and (not self.path.is_dir())
|
"mobilizon_reshare", f"migrations/{scheme}"
|
||||||
if not self.is_init:
|
)
|
||||||
self.path.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
async def _implement_db_changes(self):
|
async def _implement_db_changes(self):
|
||||||
migration_queries_location = pkg_resources.resource_filename(
|
logging.info("Performing aerich migrations.")
|
||||||
"mobilizon_reshare", "migrations"
|
|
||||||
)
|
|
||||||
command = Command(
|
command = Command(
|
||||||
tortoise_config=TORTOISE_ORM,
|
tortoise_config=get_tortoise_orm(),
|
||||||
app="models",
|
app="models",
|
||||||
location=migration_queries_location,
|
location=self.get_migration_location(),
|
||||||
)
|
)
|
||||||
await command.init()
|
await command.init()
|
||||||
migrations = await command.upgrade()
|
migrations = await command.upgrade()
|
||||||
if migrations:
|
if migrations:
|
||||||
logging.warning("Updated database to latest version")
|
logging.info("Updated database to latest version")
|
||||||
|
|
||||||
async def setup(self):
|
async def setup(self):
|
||||||
await self._implement_db_changes()
|
await self._implement_db_changes()
|
||||||
await Tortoise.init(
|
await Tortoise.init(config=get_tortoise_orm(),)
|
||||||
config=TORTOISE_ORM,
|
await Tortoise.generate_schemas()
|
||||||
)
|
|
||||||
if not self.is_init:
|
|
||||||
await Tortoise.generate_schemas()
|
|
||||||
self.is_init = True
|
|
||||||
logger.info(f"Successfully initialized database at {self.path}")
|
|
||||||
|
|
||||||
await update_publishers(publisher_names)
|
await update_publishers(publisher_names)
|
||||||
|
|
||||||
|
|
||||||
|
class MoReSQLiteDB(MoReDB):
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
if (
|
||||||
|
get_db_url().path
|
||||||
|
): # if in-memory sqlite there's no path so nothing has to be initialized
|
||||||
|
self.path = Path(get_db_url().path)
|
||||||
|
# TODO: Check if DB is openable/"queriable"
|
||||||
|
self.is_init = self.path.exists() and (not self.path.is_dir())
|
||||||
|
if not self.is_init:
|
||||||
|
self.path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
async def tear_down():
|
async def tear_down():
|
||||||
return await Tortoise.close_connections()
|
return await Tortoise.close_connections()
|
||||||
|
|
||||||
|
|
||||||
|
async def init(init_logging=True):
|
||||||
|
|
||||||
|
if init_logging:
|
||||||
|
dictConfig(get_settings()["logging"])
|
||||||
|
|
||||||
|
# init storage
|
||||||
|
url = get_db_url()
|
||||||
|
if url.scheme == "sqlite":
|
||||||
|
db = MoReSQLiteDB()
|
||||||
|
else:
|
||||||
|
db = MoReDB()
|
||||||
|
await db.setup()
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from tortoise.contrib.pydantic import pydantic_model_creator
|
||||||
|
|
||||||
|
from mobilizon_reshare.models.event import Event
|
||||||
|
from mobilizon_reshare.storage.db import init as init_db, get_db_url
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
event_pydantic = pydantic_model_creator(Event)
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def check_database():
|
||||||
|
url = get_db_url()
|
||||||
|
if url.scheme == "sqlite":
|
||||||
|
logger.warning(
|
||||||
|
"Database is SQLite. This might create issues when running the web application. Please use a "
|
||||||
|
"PostgreSQL or MariaDB backend."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register_endpoints(app):
|
||||||
|
@app.get("/events", status_code=200)
|
||||||
|
async def get_event():
|
||||||
|
|
||||||
|
return await event_pydantic.from_queryset(Event.all())
|
||||||
|
|
||||||
|
|
||||||
|
@app.on_event("startup")
|
||||||
|
async def init_app(init_logging=True):
|
||||||
|
check_database()
|
||||||
|
await init_db(init_logging=init_logging)
|
||||||
|
register_endpoints(app)
|
||||||
|
return app
|
|
@ -14,8 +14,8 @@ tomlkit = "*"
|
||||||
tortoise-orm = "*"
|
tortoise-orm = "*"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
asyncmy = ["asyncmy"]
|
|
||||||
asyncpg = ["asyncpg"]
|
asyncpg = ["asyncpg"]
|
||||||
|
asyncmy = ["asyncmy"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiosqlite"
|
name = "aiosqlite"
|
||||||
|
@ -36,6 +36,23 @@ category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyio"
|
||||||
|
version = "3.6.1"
|
||||||
|
description = "High level compatibility layer for multiple asynchronous event loop implementations"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6.2"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
idna = ">=2.8"
|
||||||
|
sniffio = ">=1.1"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
doc = ["packaging", "sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"]
|
||||||
|
test = ["coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "contextlib2", "uvloop (<0.15)", "mock (>=4)", "uvloop (>=0.15)"]
|
||||||
|
trio = ["trio (>=0.16)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "appdirs"
|
name = "appdirs"
|
||||||
version = "1.4.4"
|
version = "1.4.4"
|
||||||
|
@ -55,6 +72,19 @@ python-versions = ">=3.6"
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
python-dateutil = ">=2.7.0"
|
python-dateutil = ">=2.7.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "asyncpg"
|
||||||
|
version = "0.26.0"
|
||||||
|
description = "An asyncio PostgreSQL driver"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
dev = ["Cython (>=0.29.24,<0.30.0)", "pytest (>=6.0)", "Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "pycodestyle (>=2.7.0,<2.8.0)", "flake8 (>=3.9.2,<3.10.0)", "uvloop (>=0.15.3)"]
|
||||||
|
docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)"]
|
||||||
|
test = ["pycodestyle (>=2.7.0,<2.8.0)", "flake8 (>=3.9.2,<3.10.0)", "uvloop (>=0.15.3)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "asynctest"
|
name = "asynctest"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
|
@ -151,7 +181,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "coverage"
|
name = "coverage"
|
||||||
version = "6.4.1"
|
version = "6.5.0"
|
||||||
description = "Code coverage measurement for Python"
|
description = "Code coverage measurement for Python"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
|
@ -222,6 +252,70 @@ python-versions = "*"
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
requests = "*"
|
requests = "*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastapi"
|
||||||
|
version = "0.85.0"
|
||||||
|
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0"
|
||||||
|
starlette = "0.20.4"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
all = ["email-validator (>=1.1.1,<2.0.0)", "itsdangerous (>=1.1.0,<3.0.0)", "jinja2 (>=2.11.2,<4.0.0)", "orjson (>=3.2.1,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "requests (>=2.24.0,<3.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)", "uvicorn[standard] (>=0.12.0,<0.19.0)"]
|
||||||
|
dev = ["autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "pre-commit (>=2.17.0,<3.0.0)", "uvicorn[standard] (>=0.12.0,<0.19.0)"]
|
||||||
|
doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer (>=0.4.1,<0.7.0)"]
|
||||||
|
test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.8.0)", "databases[sqlite] (>=0.3.2,<0.7.0)", "email-validator (>=1.1.1,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.23.0,<0.24.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.971)", "orjson (>=3.2.1,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "pytest (>=7.1.3,<8.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "requests (>=2.24.0,<3.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "types-orjson (==3.6.2)", "types-ujson (==5.4.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "h11"
|
||||||
|
version = "0.12.0"
|
||||||
|
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "httpcore"
|
||||||
|
version = "0.15.0"
|
||||||
|
description = "A minimal low-level HTTP client."
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
anyio = ">=3.0.0,<4.0.0"
|
||||||
|
certifi = "*"
|
||||||
|
h11 = ">=0.11,<0.13"
|
||||||
|
sniffio = ">=1.0.0,<2.0.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
http2 = ["h2 (>=3,<5)"]
|
||||||
|
socks = ["socksio (>=1.0.0,<2.0.0)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "httpx"
|
||||||
|
version = "0.23.0"
|
||||||
|
description = "The next generation HTTP client."
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
certifi = "*"
|
||||||
|
httpcore = ">=0.15.0,<0.16.0"
|
||||||
|
rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
|
||||||
|
sniffio = "*"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
brotli = ["brotlicffi", "brotli"]
|
||||||
|
cli = ["click (>=8.0.0,<9.0.0)", "rich (>=10,<13)", "pygments (>=2.0.0,<3.0.0)"]
|
||||||
|
http2 = ["h2 (>=3,<5)"]
|
||||||
|
socks = ["socksio (>=1.0.0,<2.0.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "3.3"
|
version = "3.3"
|
||||||
|
@ -463,7 +557,7 @@ coverage = {version = ">=5.2.1", extras = ["toml"]}
|
||||||
pytest = ">=4.6"
|
pytest = ">=4.6"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"]
|
testing = ["virtualenv", "pytest-xdist", "six", "process-tests", "hunter", "fields"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytest-lazy-fixture"
|
name = "pytest-lazy-fixture"
|
||||||
|
@ -559,6 +653,20 @@ urllib3 = ">=1.25.10"
|
||||||
[package.extras]
|
[package.extras]
|
||||||
tests = ["coverage (>=3.7.1,<6.0.0)", "pytest-cov", "pytest-localserver", "flake8", "types-mock", "types-requests", "types-six", "pytest (>=4.6,<5.0)", "pytest (>=4.6)", "mypy"]
|
tests = ["coverage (>=3.7.1,<6.0.0)", "pytest-cov", "pytest-localserver", "flake8", "types-mock", "types-requests", "types-six", "pytest (>=4.6,<5.0)", "pytest (>=4.6)", "mypy"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rfc3986"
|
||||||
|
version = "1.5.0"
|
||||||
|
description = "Validating URI References per RFC 3986"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
idna = {version = "*", optional = true, markers = "extra == \"idna2008\""}
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
idna2008 = ["idna"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "six"
|
name = "six"
|
||||||
version = "1.16.0"
|
version = "1.16.0"
|
||||||
|
@ -567,6 +675,14 @@ category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sniffio"
|
||||||
|
version = "1.3.0"
|
||||||
|
description = "Sniff out which async library your code is running under"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "snowballstemmer"
|
name = "snowballstemmer"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
|
@ -657,8 +773,8 @@ optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.5"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
lint = ["flake8", "mypy", "docutils-stubs"]
|
|
||||||
test = ["pytest"]
|
test = ["pytest"]
|
||||||
|
lint = ["docutils-stubs", "mypy", "flake8"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sphinxcontrib-devhelp"
|
name = "sphinxcontrib-devhelp"
|
||||||
|
@ -669,8 +785,8 @@ optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.5"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
lint = ["flake8", "mypy", "docutils-stubs"]
|
|
||||||
test = ["pytest"]
|
test = ["pytest"]
|
||||||
|
lint = ["docutils-stubs", "mypy", "flake8"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sphinxcontrib-htmlhelp"
|
name = "sphinxcontrib-htmlhelp"
|
||||||
|
@ -681,8 +797,8 @@ optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
lint = ["flake8", "mypy", "docutils-stubs"]
|
test = ["html5lib", "pytest"]
|
||||||
test = ["pytest", "html5lib"]
|
lint = ["docutils-stubs", "mypy", "flake8"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sphinxcontrib-jsmath"
|
name = "sphinxcontrib-jsmath"
|
||||||
|
@ -693,7 +809,7 @@ optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.5"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
test = ["pytest", "flake8", "mypy"]
|
test = ["mypy", "flake8", "pytest"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sphinxcontrib-napoleon"
|
name = "sphinxcontrib-napoleon"
|
||||||
|
@ -716,8 +832,8 @@ optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.5"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
lint = ["flake8", "mypy", "docutils-stubs"]
|
|
||||||
test = ["pytest"]
|
test = ["pytest"]
|
||||||
|
lint = ["docutils-stubs", "mypy", "flake8"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sphinxcontrib-serializinghtml"
|
name = "sphinxcontrib-serializinghtml"
|
||||||
|
@ -731,6 +847,21 @@ python-versions = ">=3.5"
|
||||||
lint = ["flake8", "mypy", "docutils-stubs"]
|
lint = ["flake8", "mypy", "docutils-stubs"]
|
||||||
test = ["pytest"]
|
test = ["pytest"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "starlette"
|
||||||
|
version = "0.20.4"
|
||||||
|
description = "The little ASGI library that shines."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
anyio = ">=3.4.0,<5"
|
||||||
|
typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "text-unidecode"
|
name = "text-unidecode"
|
||||||
version = "1.3"
|
version = "1.3"
|
||||||
|
@ -773,6 +904,7 @@ python-versions = ">=3.7,<4.0"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
aiosqlite = ">=0.16.0,<0.18.0"
|
aiosqlite = ">=0.16.0,<0.18.0"
|
||||||
|
asyncpg = {version = "*", optional = true, markers = "extra == \"asyncpg\""}
|
||||||
iso8601 = ">=1.0.2,<2.0.0"
|
iso8601 = ">=1.0.2,<2.0.0"
|
||||||
pypika-tortoise = ">=0.1.6,<0.2.0"
|
pypika-tortoise = ">=0.1.6,<0.2.0"
|
||||||
pytz = "*"
|
pytz = "*"
|
||||||
|
@ -832,6 +964,21 @@ brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"]
|
||||||
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
|
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
|
||||||
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uvicorn"
|
||||||
|
version = "0.18.3"
|
||||||
|
description = "The lightning-fast ASGI server."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
click = ">=7.0"
|
||||||
|
h11 = ">=0.8"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
standard = ["colorama (>=0.4)", "httptools (>=0.4.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zipp"
|
name = "zipp"
|
||||||
version = "3.8.0"
|
version = "3.8.0"
|
||||||
|
@ -847,21 +994,16 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.9"
|
python-versions = "^3.9"
|
||||||
content-hash = "9eccb917a541dec588301184f58ffa68a14c6def02bb988d2d6241598b8539ae"
|
content-hash = "56fecab376d8a94ff6dd64f853128388f458001b6f4eb63dddc3fd4bb2773df1"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
aerich = [
|
aerich = []
|
||||||
{file = "aerich-0.6.3-py3-none-any.whl", hash = "sha256:d45f98214ed54b8ec9be949df264c5b0b9e8b79d8f8ba9e68714675bc81a5574"},
|
|
||||||
{file = "aerich-0.6.3.tar.gz", hash = "sha256:96ac087922048470687264125cb9dfeaade982219c78e6a9b91a0fb67eaa1cd1"},
|
|
||||||
]
|
|
||||||
aiosqlite = [
|
aiosqlite = [
|
||||||
{file = "aiosqlite-0.17.0-py3-none-any.whl", hash = "sha256:6c49dc6d3405929b1d08eeccc72306d3677503cc5e5e43771efc1e00232e8231"},
|
{file = "aiosqlite-0.17.0-py3-none-any.whl", hash = "sha256:6c49dc6d3405929b1d08eeccc72306d3677503cc5e5e43771efc1e00232e8231"},
|
||||||
{file = "aiosqlite-0.17.0.tar.gz", hash = "sha256:f0e6acc24bc4864149267ac82fb46dfb3be4455f99fe21df82609cc6e6baee51"},
|
{file = "aiosqlite-0.17.0.tar.gz", hash = "sha256:f0e6acc24bc4864149267ac82fb46dfb3be4455f99fe21df82609cc6e6baee51"},
|
||||||
]
|
]
|
||||||
alabaster = [
|
alabaster = []
|
||||||
{file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"},
|
anyio = []
|
||||||
{file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"},
|
|
||||||
]
|
|
||||||
appdirs = [
|
appdirs = [
|
||||||
{file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
|
{file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
|
||||||
{file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
|
{file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
|
||||||
|
@ -870,6 +1012,7 @@ arrow = [
|
||||||
{file = "arrow-1.1.1-py3-none-any.whl", hash = "sha256:77a60a4db5766d900a2085ce9074c5c7b8e2c99afeaa98ad627637ff6f292510"},
|
{file = "arrow-1.1.1-py3-none-any.whl", hash = "sha256:77a60a4db5766d900a2085ce9074c5c7b8e2c99afeaa98ad627637ff6f292510"},
|
||||||
{file = "arrow-1.1.1.tar.gz", hash = "sha256:dee7602f6c60e3ec510095b5e301441bc56288cb8f51def14dcb3079f623823a"},
|
{file = "arrow-1.1.1.tar.gz", hash = "sha256:dee7602f6c60e3ec510095b5e301441bc56288cb8f51def14dcb3079f623823a"},
|
||||||
]
|
]
|
||||||
|
asyncpg = []
|
||||||
asynctest = [
|
asynctest = [
|
||||||
{file = "asynctest-0.13.0-py3-none-any.whl", hash = "sha256:5da6118a7e6d6b54d83a8f7197769d046922a44d2a99c21382f0a6e4fadae676"},
|
{file = "asynctest-0.13.0-py3-none-any.whl", hash = "sha256:5da6118a7e6d6b54d83a8f7197769d046922a44d2a99c21382f0a6e4fadae676"},
|
||||||
{file = "asynctest-0.13.0.tar.gz", hash = "sha256:c27862842d15d83e6a34eb0b2866c323880eb3a75e4485b079ea11748fd77fac"},
|
{file = "asynctest-0.13.0.tar.gz", hash = "sha256:c27862842d15d83e6a34eb0b2866c323880eb3a75e4485b079ea11748fd77fac"},
|
||||||
|
@ -892,24 +1035,21 @@ charset-normalizer = [
|
||||||
click = []
|
click = []
|
||||||
colorama = []
|
colorama = []
|
||||||
coverage = []
|
coverage = []
|
||||||
css-html-js-minify = [
|
css-html-js-minify = []
|
||||||
{file = "css-html-js-minify-2.5.5.zip", hash = "sha256:4a9f11f7e0496f5284d12111f3ba4ff5ff2023d12f15d195c9c48bd97013746c"},
|
|
||||||
{file = "css_html_js_minify-2.5.5-py2.py3-none-any.whl", hash = "sha256:3da9d35ac0db8ca648c1b543e0e801d7ca0bab9e6bfd8418fee59d5ae001727a"},
|
|
||||||
{file = "css_html_js_minify-2.5.5-py3.6.egg", hash = "sha256:4704e04a0cd6dd56d61bbfa3bfffc630da6b2284be33519be0b456672e2a2438"},
|
|
||||||
]
|
|
||||||
dictdiffer = [
|
dictdiffer = [
|
||||||
{file = "dictdiffer-0.9.0-py2.py3-none-any.whl", hash = "sha256:442bfc693cfcadaf46674575d2eba1c53b42f5e404218ca2c2ff549f2df56595"},
|
{file = "dictdiffer-0.9.0-py2.py3-none-any.whl", hash = "sha256:442bfc693cfcadaf46674575d2eba1c53b42f5e404218ca2c2ff549f2df56595"},
|
||||||
{file = "dictdiffer-0.9.0.tar.gz", hash = "sha256:17bacf5fbfe613ccf1b6d512bd766e6b21fb798822a133aa86098b8ac9997578"},
|
{file = "dictdiffer-0.9.0.tar.gz", hash = "sha256:17bacf5fbfe613ccf1b6d512bd766e6b21fb798822a133aa86098b8ac9997578"},
|
||||||
]
|
]
|
||||||
docutils = [
|
docutils = []
|
||||||
{file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"},
|
|
||||||
{file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"},
|
|
||||||
]
|
|
||||||
dynaconf = []
|
dynaconf = []
|
||||||
facebook-sdk = [
|
facebook-sdk = []
|
||||||
{file = "facebook-sdk-3.1.0.tar.gz", hash = "sha256:cabcd2e69ea3d9f042919c99b353df7aa1e2be86d040121f6e9f5e63c1cf0f8d"},
|
fastapi = []
|
||||||
{file = "facebook_sdk-3.1.0-py2.py3-none-any.whl", hash = "sha256:2e987b3e0f466a6f4ee77b935eb023dba1384134f004a2af21f1cfff7fe0806e"},
|
h11 = [
|
||||||
|
{file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
|
||||||
|
{file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"},
|
||||||
]
|
]
|
||||||
|
httpcore = []
|
||||||
|
httpx = []
|
||||||
idna = [
|
idna = [
|
||||||
{file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
|
{file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
|
||||||
{file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
|
{file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
|
||||||
|
@ -920,62 +1060,15 @@ iniconfig = [
|
||||||
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
|
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
|
||||||
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
|
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
|
||||||
]
|
]
|
||||||
iso8601 = []
|
iso8601 = [
|
||||||
jinja2 = [
|
{file = "iso8601-1.0.2-py3-none-any.whl", hash = "sha256:d7bc01b1c2a43b259570bb307f057abc578786ea734ba2b87b836c5efc5bd443"},
|
||||||
{file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
|
{file = "iso8601-1.0.2.tar.gz", hash = "sha256:27f503220e6845d9db954fb212b95b0362d8b7e6c1b2326a87061c3de93594b1"},
|
||||||
{file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
|
|
||||||
]
|
]
|
||||||
|
jinja2 = []
|
||||||
lxml = []
|
lxml = []
|
||||||
markdownify = [
|
markdownify = []
|
||||||
{file = "markdownify-0.10.3-py3-none-any.whl", hash = "sha256:edad0ad3896ec7460d05537ad804bbb3614877c6cd0df27b56dee218236d9ce2"},
|
markupsafe = []
|
||||||
{file = "markdownify-0.10.3.tar.gz", hash = "sha256:782e310390cd5e4bde7543ceb644598c78b9824ee9f8d7ef9f9f4f8782e46974"},
|
oauthlib = []
|
||||||
]
|
|
||||||
markupsafe = [
|
|
||||||
{file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"},
|
|
||||||
{file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"},
|
|
||||||
{file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"},
|
|
||||||
]
|
|
||||||
oauthlib = [
|
|
||||||
{file = "oauthlib-3.2.0-py3-none-any.whl", hash = "sha256:6db33440354787f9b7f3a6dbd4febf5d0f93758354060e802f6c06cb493022fe"},
|
|
||||||
{file = "oauthlib-3.2.0.tar.gz", hash = "sha256:23a8208d75b902797ea29fd31fa80a15ed9dc2c6c16fe73f5d346f83f6fa27a2"},
|
|
||||||
]
|
|
||||||
packaging = [
|
packaging = [
|
||||||
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
|
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
|
||||||
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
|
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
|
||||||
|
@ -984,23 +1077,14 @@ pluggy = [
|
||||||
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
|
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
|
||||||
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
|
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
|
||||||
]
|
]
|
||||||
pockets = [
|
pockets = []
|
||||||
{file = "pockets-0.9.1-py2.py3-none-any.whl", hash = "sha256:68597934193c08a08eb2bf6a1d85593f627c22f9b065cc727a4f03f669d96d86"},
|
|
||||||
{file = "pockets-0.9.1.tar.gz", hash = "sha256:9320f1a3c6f7a9133fe3b571f283bcf3353cd70249025ae8d618e40e9f7e92b3"},
|
|
||||||
]
|
|
||||||
py = [
|
py = [
|
||||||
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
|
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
|
||||||
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
|
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
|
||||||
]
|
]
|
||||||
pydantic = []
|
pydantic = []
|
||||||
pygments = [
|
pygments = []
|
||||||
{file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"},
|
pyparsing = []
|
||||||
{file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"},
|
|
||||||
]
|
|
||||||
pyparsing = [
|
|
||||||
{file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
|
|
||||||
{file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
|
|
||||||
]
|
|
||||||
pypika-tortoise = []
|
pypika-tortoise = []
|
||||||
pytest = [
|
pytest = [
|
||||||
{file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"},
|
{file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"},
|
||||||
|
@ -1019,10 +1103,7 @@ python-dateutil = [
|
||||||
{file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
|
{file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
|
||||||
{file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
|
{file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
|
||||||
]
|
]
|
||||||
python-slugify = [
|
python-slugify = []
|
||||||
{file = "python-slugify-6.1.2.tar.gz", hash = "sha256:272d106cb31ab99b3496ba085e3fea0e9e76dcde967b5e9992500d1f785ce4e1"},
|
|
||||||
{file = "python_slugify-6.1.2-py2.py3-none-any.whl", hash = "sha256:7b2c274c308b62f4269a9ba701aa69a797e9bca41aeee5b3a9e79e36b6656927"},
|
|
||||||
]
|
|
||||||
pytz = [
|
pytz = [
|
||||||
{file = "pytz-2022.1-py2.py3-none-any.whl", hash = "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"},
|
{file = "pytz-2022.1-py2.py3-none-any.whl", hash = "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"},
|
||||||
{file = "pytz-2022.1.tar.gz", hash = "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7"},
|
{file = "pytz-2022.1.tar.gz", hash = "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7"},
|
||||||
|
@ -1031,66 +1112,33 @@ requests = [
|
||||||
{file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
|
{file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
|
||||||
{file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
|
{file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
|
||||||
]
|
]
|
||||||
requests-oauthlib = [
|
requests-oauthlib = []
|
||||||
{file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"},
|
|
||||||
{file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"},
|
|
||||||
]
|
|
||||||
responses = [
|
responses = [
|
||||||
{file = "responses-0.13.4-py2.py3-none-any.whl", hash = "sha256:d8d0f655710c46fd3513b9202a7f0dcedd02ca0f8cf4976f27fa8ab5b81e656d"},
|
{file = "responses-0.13.4-py2.py3-none-any.whl", hash = "sha256:d8d0f655710c46fd3513b9202a7f0dcedd02ca0f8cf4976f27fa8ab5b81e656d"},
|
||||||
{file = "responses-0.13.4.tar.gz", hash = "sha256:9476775d856d3c24ae660bbebe29fb6d789d4ad16acd723efbfb6ee20990b899"},
|
{file = "responses-0.13.4.tar.gz", hash = "sha256:9476775d856d3c24ae660bbebe29fb6d789d4ad16acd723efbfb6ee20990b899"},
|
||||||
]
|
]
|
||||||
|
rfc3986 = [
|
||||||
|
{file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"},
|
||||||
|
{file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"},
|
||||||
|
]
|
||||||
six = [
|
six = [
|
||||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||||
]
|
]
|
||||||
snowballstemmer = [
|
sniffio = []
|
||||||
{file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"},
|
snowballstemmer = []
|
||||||
{file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"},
|
soupsieve = []
|
||||||
]
|
sphinx = []
|
||||||
soupsieve = [
|
sphinx-autodoc-typehints = []
|
||||||
{file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"},
|
sphinx-material = []
|
||||||
{file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"},
|
sphinxcontrib-applehelp = []
|
||||||
]
|
sphinxcontrib-devhelp = []
|
||||||
sphinx = [
|
sphinxcontrib-htmlhelp = []
|
||||||
{file = "Sphinx-4.4.0-py3-none-any.whl", hash = "sha256:5da895959511473857b6d0200f56865ed62c31e8f82dd338063b84ec022701fe"},
|
sphinxcontrib-jsmath = []
|
||||||
{file = "Sphinx-4.4.0.tar.gz", hash = "sha256:6caad9786055cb1fa22b4a365c1775816b876f91966481765d7d50e9f0dd35cc"},
|
sphinxcontrib-napoleon = []
|
||||||
]
|
sphinxcontrib-qthelp = []
|
||||||
sphinx-autodoc-typehints = [
|
sphinxcontrib-serializinghtml = []
|
||||||
{file = "sphinx_autodoc_typehints-1.17.1-py3-none-any.whl", hash = "sha256:f16491cad05a13f4825ecdf9ee4ff02925d9a3b1cf103d4d02f2f81802cce653"},
|
starlette = []
|
||||||
{file = "sphinx_autodoc_typehints-1.17.1.tar.gz", hash = "sha256:844d7237d3f6280b0416f5375d9556cfd84df1945356fcc34b82e8aaacab40f3"},
|
|
||||||
]
|
|
||||||
sphinx-material = [
|
|
||||||
{file = "sphinx_material-0.0.35-py3-none-any.whl", hash = "sha256:a62a0a48d4c32edc260f9bdbca658e7d149beb10e1d338848b0076bb13be0775"},
|
|
||||||
{file = "sphinx_material-0.0.35.tar.gz", hash = "sha256:27f0f1084aa0201b43879aef24a0521b78dc8df4942b003a4e7d79ab11515852"},
|
|
||||||
]
|
|
||||||
sphinxcontrib-applehelp = [
|
|
||||||
{file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"},
|
|
||||||
{file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"},
|
|
||||||
]
|
|
||||||
sphinxcontrib-devhelp = [
|
|
||||||
{file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"},
|
|
||||||
{file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"},
|
|
||||||
]
|
|
||||||
sphinxcontrib-htmlhelp = [
|
|
||||||
{file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"},
|
|
||||||
{file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"},
|
|
||||||
]
|
|
||||||
sphinxcontrib-jsmath = [
|
|
||||||
{file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"},
|
|
||||||
{file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"},
|
|
||||||
]
|
|
||||||
sphinxcontrib-napoleon = [
|
|
||||||
{file = "sphinxcontrib-napoleon-0.7.tar.gz", hash = "sha256:407382beed396e9f2d7f3043fad6afda95719204a1e1a231ac865f40abcbfcf8"},
|
|
||||||
{file = "sphinxcontrib_napoleon-0.7-py2.py3-none-any.whl", hash = "sha256:711e41a3974bdf110a484aec4c1a556799eb0b3f3b897521a018ad7e2db13fef"},
|
|
||||||
]
|
|
||||||
sphinxcontrib-qthelp = [
|
|
||||||
{file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"},
|
|
||||||
{file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"},
|
|
||||||
]
|
|
||||||
sphinxcontrib-serializinghtml = [
|
|
||||||
{file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"},
|
|
||||||
{file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"},
|
|
||||||
]
|
|
||||||
text-unidecode = [
|
text-unidecode = [
|
||||||
{file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"},
|
{file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"},
|
||||||
{file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"},
|
{file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"},
|
||||||
|
@ -1107,12 +1155,7 @@ tweepy = [
|
||||||
{file = "tweepy-4.4.0.tar.gz", hash = "sha256:8d4b4520271b796fa7efc4c5d5ef3228af4d79f6a4d3ace3900b2778ed8f6f1c"},
|
{file = "tweepy-4.4.0.tar.gz", hash = "sha256:8d4b4520271b796fa7efc4c5d5ef3228af4d79f6a4d3ace3900b2778ed8f6f1c"},
|
||||||
]
|
]
|
||||||
typing-extensions = []
|
typing-extensions = []
|
||||||
unidecode = [
|
unidecode = []
|
||||||
{file = "Unidecode-1.3.4-py3-none-any.whl", hash = "sha256:afa04efcdd818a93237574791be9b2817d7077c25a068b00f8cff7baa4e59257"},
|
|
||||||
{file = "Unidecode-1.3.4.tar.gz", hash = "sha256:8e4352fb93d5a735c788110d2e7ac8e8031eb06ccbfe8d324ab71735015f9342"},
|
|
||||||
]
|
|
||||||
urllib3 = []
|
urllib3 = []
|
||||||
zipp = [
|
uvicorn = []
|
||||||
{file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"},
|
zipp = []
|
||||||
{file = "zipp-3.8.0.tar.gz", hash = "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad"},
|
|
||||||
]
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ license = "Coopyleft"
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.9"
|
python = "^3.9"
|
||||||
dynaconf = "~3.1"
|
dynaconf = "~3.1"
|
||||||
tortoise-orm = "~0.19"
|
tortoise-orm = {extras = ["asyncpg"], version = "^0.19.2"}
|
||||||
aiosqlite = "~0.17"
|
aiosqlite = "~0.17"
|
||||||
Jinja2 = "~3.1"
|
Jinja2 = "~3.1"
|
||||||
requests = "~2.27"
|
requests = "~2.27"
|
||||||
|
@ -23,6 +23,8 @@ appdirs = "~1.4"
|
||||||
tweepy = "~4.4"
|
tweepy = "~4.4"
|
||||||
facebook-sdk = "~3.1"
|
facebook-sdk = "~3.1"
|
||||||
aerich = "~0.6"
|
aerich = "~0.6"
|
||||||
|
fastapi = "^0.85.0"
|
||||||
|
uvicorn = "^0.18.3"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
responses = "~0.13"
|
responses = "~0.13"
|
||||||
|
@ -35,6 +37,9 @@ Sphinx = "~4.4"
|
||||||
sphinxcontrib-napoleon = "~0.7"
|
sphinxcontrib-napoleon = "~0.7"
|
||||||
sphinx-material = "~0.0"
|
sphinx-material = "~0.0"
|
||||||
sphinx-autodoc-typehints = "~1.17"
|
sphinx-autodoc-typehints = "~1.17"
|
||||||
|
httpx = "^0.23.0"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core>=1.0.0"]
|
requires = ["poetry-core>=1.0.0"]
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
[default.publisher.telegram]
|
||||||
|
active=false
|
||||||
|
chat_id="xxx"
|
||||||
|
token="xxx"
|
||||||
|
username="xxx"
|
||||||
|
[default.publisher.zulip]
|
||||||
|
active=false
|
||||||
|
instance="xxx"
|
||||||
|
chat_id="xxx"
|
||||||
|
subject="xxx"
|
||||||
|
bot_token="xxx"
|
||||||
|
bot_email="xxx"
|
||||||
|
[default.publisher.twitter]
|
||||||
|
active=false
|
||||||
|
api_key="xxx"
|
||||||
|
api_key_secret="xxx"
|
||||||
|
access_token="xxx"
|
||||||
|
access_secret="xxx"
|
||||||
|
[default.publisher.mastodon]
|
||||||
|
active=false
|
||||||
|
instance="xxx"
|
||||||
|
token="xxx"
|
||||||
|
name="xxx"
|
||||||
|
toot_length=500
|
||||||
|
|
||||||
|
[default.publisher.facebook]
|
||||||
|
|
||||||
|
active=false
|
||||||
|
page_access_token="xxx"
|
||||||
|
|
||||||
|
[default.notifier.telegram]
|
||||||
|
active=false
|
||||||
|
chat_id="xxx"
|
||||||
|
token="xxx"
|
||||||
|
username="xxx"
|
||||||
|
[default.notifier.zulip]
|
||||||
|
active=false
|
||||||
|
instance="xxx"
|
||||||
|
chat_id="xxx"
|
||||||
|
subject="xxx"
|
||||||
|
bot_token="xxx"
|
||||||
|
bot_email="xxx"
|
||||||
|
[default.notifier.twitter]
|
||||||
|
active=false
|
||||||
|
api_key="xxx"
|
||||||
|
api_key_secret="xxx"
|
||||||
|
access_token="xxx"
|
||||||
|
access_secret="xxx"
|
||||||
|
[default.notifier.mastodon]
|
||||||
|
active=false
|
||||||
|
|
||||||
|
[default.notifier.facebook]
|
||||||
|
active=false
|
||||||
|
page_access_token="xxx"
|
|
@ -0,0 +1,33 @@
|
||||||
|
[default]
|
||||||
|
debug = false
|
||||||
|
default = true
|
||||||
|
local_state_dir = "/var/lib/mobilizon-reshare"
|
||||||
|
#db_path = "@format {this.local_state_dir}/events.db"
|
||||||
|
db_url = "@format postgres://mobilizon_reshare:mobilizon_reshare@db:5432/mobilizon_reshare"
|
||||||
|
|
||||||
|
[default.source.mobilizon]
|
||||||
|
url="https://some-mobilizon.com/api"
|
||||||
|
group="my_group"
|
||||||
|
|
||||||
|
[default.selection]
|
||||||
|
strategy = "next_event"
|
||||||
|
|
||||||
|
[default.selection.strategy_options]
|
||||||
|
break_between_events_in_minutes = 360
|
||||||
|
|
||||||
|
[default.logging]
|
||||||
|
version = 1
|
||||||
|
disable_existing_loggers = false
|
||||||
|
|
||||||
|
[default.logging.formatters.standard]
|
||||||
|
format = '[%(asctime)s] [%(levelno)s] [%(levelname)s] %(message)s'
|
||||||
|
|
||||||
|
[default.logging.handlers.console]
|
||||||
|
level = "DEBUG"
|
||||||
|
class = "logging.StreamHandler"
|
||||||
|
formatter = "standard"
|
||||||
|
stream = "ext://sys.stderr"
|
||||||
|
|
||||||
|
[default.logging.root]
|
||||||
|
level = "DEBUG"
|
||||||
|
handlers = ['console']
|
|
@ -0,0 +1,63 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
echo "This script currently doesn't work correctly due to a bug in aerich. Do not use until said bug
|
||||||
|
is fixed"
|
||||||
|
echo "For more info: https://github.com/tortoise/aerich/issues/270"
|
||||||
|
exit
|
||||||
|
get_abs_filename() {
|
||||||
|
# $1 : relative filename
|
||||||
|
echo "$(cd "$(dirname "$1")" && pwd)/"
|
||||||
|
}
|
||||||
|
|
||||||
|
PROJECT_DIR="$(get_abs_filename $0)/.."
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
# cleaning sqlite db
|
||||||
|
echo "Removing /tmp/foo"
|
||||||
|
rm -rf /tmp/tmp.db
|
||||||
|
|
||||||
|
# shutting down postgres container
|
||||||
|
cd $PROJECT_DIR
|
||||||
|
docker-compose -f docker-compose-migration.yml down
|
||||||
|
}
|
||||||
|
|
||||||
|
# making sure we leave the system clean
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
|
||||||
|
poetry install
|
||||||
|
|
||||||
|
# I activate the env instead of using poetry run because the pyproject in the migration folder gives it problems
|
||||||
|
. "$(poetry env info -p)/bin/activate"
|
||||||
|
|
||||||
|
# I create a new SQLite db to run the migrations and generate a new one
|
||||||
|
echo "Generating SQLite migrations"
|
||||||
|
export DYNACONF_DB_URL="sqlite:///tmp/tmp.db"
|
||||||
|
cd "$PROJECT_DIR/mobilizon_reshare/migrations/sqlite/"
|
||||||
|
|
||||||
|
aerich upgrade
|
||||||
|
aerich migrate
|
||||||
|
|
||||||
|
# I use a dedicated docker-compose file to spin up a postgres instance, connect to it, run the migrations and generate a
|
||||||
|
# new one
|
||||||
|
|
||||||
|
echo "Generating postgres migrations"
|
||||||
|
export DYNACONF_DB_URL="postgres://mobilizon_reshare:mobilizon_reshare@localhost:5432/mobilizon_reshare"
|
||||||
|
cd $PROJECT_DIR
|
||||||
|
|
||||||
|
docker-compose -f docker-compose-migration.yml up -d
|
||||||
|
|
||||||
|
cd "$PROJECT_DIR/mobilizon_reshare/migrations/postgres/"
|
||||||
|
until [ "$(docker inspect mo-re_db_1 --format='{{json .State.Health.Status}}')" = "\"healthy\"" ];
|
||||||
|
do
|
||||||
|
echo "Waiting for postgres"
|
||||||
|
if [ "$(docker inspect mo-re_db_1 --format='{{json .State.Health.Status}}')" = "\"healthy\"" ]
|
||||||
|
then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1s
|
||||||
|
done
|
||||||
|
aerich upgrade
|
||||||
|
aerich migrate
|
|
@ -1,9 +1,8 @@
|
||||||
import asyncio
|
|
||||||
import importlib.resources
|
import importlib.resources
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
from collections import UserList
|
from collections import UserList
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
import time
|
|
||||||
from typing import Union
|
from typing import Union
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
|
@ -130,7 +129,7 @@ async def stored_event(event) -> Event:
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function", autouse=True)
|
@pytest.fixture(scope="function", autouse=True)
|
||||||
def initialize_db_tests(request) -> None:
|
async def initialize_db_tests(request) -> None:
|
||||||
config = {
|
config = {
|
||||||
"connections": {
|
"connections": {
|
||||||
"default": os.environ.get("TORTOISE_TEST_DB", "sqlite://:memory:")
|
"default": os.environ.get("TORTOISE_TEST_DB", "sqlite://:memory:")
|
||||||
|
@ -161,10 +160,9 @@ def initialize_db_tests(request) -> None:
|
||||||
await Tortoise.init(config, _create_db=True)
|
await Tortoise.init(config, _create_db=True)
|
||||||
await Tortoise.generate_schemas(safe=False)
|
await Tortoise.generate_schemas(safe=False)
|
||||||
|
|
||||||
loop = asyncio.get_event_loop()
|
await _init_db()
|
||||||
loop.run_until_complete(_init_db())
|
yield
|
||||||
|
await Tortoise._drop_databases()
|
||||||
request.addfinalizer(lambda: loop.run_until_complete(Tortoise._drop_databases()))
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import pytest
|
||||||
|
import urllib3
|
||||||
|
from httpx import AsyncClient
|
||||||
|
|
||||||
|
from mobilizon_reshare.storage import db
|
||||||
|
from mobilizon_reshare.web.backend.main import app, register_endpoints
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def anyio_backend():
|
||||||
|
return "asyncio"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
async def client():
|
||||||
|
register_endpoints(app)
|
||||||
|
async with AsyncClient(app=app, base_url="http://test") as client:
|
||||||
|
yield client
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def mock_sqlite_db(monkeypatch):
|
||||||
|
def get_url():
|
||||||
|
return urllib3.util.parse_url("sqlite://memory")
|
||||||
|
|
||||||
|
monkeypatch.setattr(db, "get_db_url", get_url)
|
|
@ -0,0 +1,16 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from httpx import AsyncClient
|
||||||
|
|
||||||
|
from mobilizon_reshare.web.backend.main import event_pydantic
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.anyio
|
||||||
|
async def test_events(client: AsyncClient, event_model_generator):
|
||||||
|
event = event_model_generator()
|
||||||
|
await event.save()
|
||||||
|
|
||||||
|
response = await client.get("/events")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json()[0] == [json.loads(event_pydantic.from_orm(event).json())][0]
|
|
@ -0,0 +1,37 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import urllib3.util
|
||||||
|
|
||||||
|
from mobilizon_reshare.web.backend import main
|
||||||
|
from mobilizon_reshare.web.backend.main import check_database, init_app
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_database_sqlite(caplog):
|
||||||
|
with caplog.at_level(logging.WARNING):
|
||||||
|
check_database()
|
||||||
|
assert caplog.messages == [
|
||||||
|
"Database is SQLite. This might create issues when running the web application. "
|
||||||
|
"Please use a PostgreSQL or MariaDB backend."
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_check_database_cli(caplog):
|
||||||
|
with caplog.at_level(logging.WARNING):
|
||||||
|
await init_app(init_logging=False)
|
||||||
|
assert caplog.messages == [
|
||||||
|
"Database is SQLite. This might create issues when running the web application. "
|
||||||
|
"Please use a PostgreSQL or MariaDB backend."
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_check_database_postgres(caplog, monkeypatch):
|
||||||
|
def get_url():
|
||||||
|
return urllib3.util.parse_url("postgres://someone@something.it")
|
||||||
|
|
||||||
|
monkeypatch.setattr(main, "get_db_url", get_url)
|
||||||
|
with caplog.at_level(logging.WARNING):
|
||||||
|
check_database()
|
||||||
|
assert caplog.messages == []
|
Loading…
Reference in New Issue