Run scan task async via redis + sockets

This commit is contained in:
Georges-Antoine Assi
2023-08-04 14:55:08 -04:00
parent bcce180e2c
commit 017f968303
9 changed files with 127 additions and 129 deletions

View File

View File

@@ -1,5 +1,8 @@
import json
import emoji
import socketio
from rq import Queue
from redis import Redis
from logger.logger import log
from utils import fs, fastapi
@@ -7,14 +10,19 @@ from utils.exceptions import PlatformsNotFoundException, RomsNotFoundException
from handler import dbh
from models.platform import Platform
from models.rom import Rom
from handler.socket_manager import SocketManager
from handler.socket_manager import socket_server
async def scan(
sm: SocketManager, _sid: str, platforms: str, complete_rescan: bool = True
):
redis_conn = Redis()
scan_queue = Queue(connection=redis_conn)
async def scan_filesystem(platforms: str, complete_rescan: bool = True):
"""Scan platforms and roms and write them in database."""
# Connect to external socketio server
sm = socketio.AsyncRedisManager("redis://", write_only=True)
log.info(emoji.emojize(":magnifying_glass_tilted_right: Scanning "))
fs.store_default_resources()
@@ -39,7 +47,6 @@ async def scan(
await sm.emit(
"scan:scanning_platform",
{"p_name": scanned_platform.name, "p_slug": scanned_platform.slug},
ignore_queue=True,
)
dbh.add_platform(scanned_platform)
@@ -56,10 +63,10 @@ async def scan(
"scan:scanning_rom",
{
"p_slug": scanned_platform.slug,
"p_name": scanned_platform.name,
"file_name": scanned_rom.file_name,
"r_name": scanned_rom.r_name,
},
ignore_queue=True,
)
if rom_id:
@@ -69,4 +76,9 @@ async def scan(
dbh.purge_roms(scanned_platform.slug, [rom["file_name"] for rom in fs_roms])
dbh.purge_platforms(fs_platforms)
await sm.emit("scan:done")
await sm.emit("scan:done", {})
@socket_server.on("scan")
def scan_handler(_sid: str, platforms: str, complete_rescan: bool = True):
scan_queue.enqueue(scan_filesystem, platforms, complete_rescan)

View File

@@ -1,32 +1,12 @@
import socketio
class SocketManager(object):
def __new__(cls):
if not hasattr(cls, "instance"):
cls.instance = super(SocketManager, cls).__new__(cls)
return cls.instance
socket_server = socketio.AsyncServer(
cors_allowed_origins="*",
async_mode="asgi",
logger=False,
engineio_logger=False,
client_manager=socketio.AsyncRedisManager("redis://"),
)
def __init__(self):
self.server = socketio.AsyncServer(
cors_allowed_origins="*",
async_mode="asgi",
logger=False,
engineio_logger=False,
)
self.app = socketio.ASGIApp(self.server)
@property
def on(self):
return self.server.on
@property
def send(self):
return self.server.send
@property
def emit(self):
return self.server.emit
def mount_to(self, path: str, app):
app.mount(path, self.app)
socket_app = socketio.ASGIApp(socket_server)

View File

@@ -5,8 +5,8 @@ from fastapi.middleware.cors import CORSMiddleware
from fastapi_pagination import add_pagination
from config import DEV_PORT, DEV_HOST
from handler.socket_manager import SocketManager
from endpoints import scan, search, platform, rom
from handler.socket_manager import socket_app
from endpoints import search, platform, rom, scan # noqa
app = FastAPI()
app.add_middleware(
@@ -21,20 +21,7 @@ app.include_router(platform.router)
app.include_router(rom.router)
add_pagination(app)
sm = SocketManager()
sm.mount_to("/ws", app)
async def scan_handler(*args):
try:
await scan.scan(sm, *args)
except Exception as err:
await sm.emit("scan:done_ko", str(err))
raise (err)
sm.on("scan", handler=scan_handler)
app.mount("/ws", socket_app)
@app.on_event("startup")

View File

@@ -3,6 +3,7 @@ services:
mariadb:
image: mariadb:latest
container_name: mariadb
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWD}
- MYSQL_DATABASE=${DB_NAME}
@@ -10,4 +11,10 @@ services:
- MYSQL_PASSWORD=${DB_PASSWD}
ports:
- ${DB_PORT}:3306
restart: "no"
redis:
image: redis:alpine
container_name: redis
restart: unless-stopped
ports:
- ${REDIS_PORT}:6379

View File

@@ -18,3 +18,6 @@ DB_NAME=romm
DB_USER=romm
DB_PASSWD=
DB_ROOT_PASSWD=
# Redis config (development only)
REDIS_PORT=6379

View File

@@ -23,12 +23,21 @@ socket.on("scan:scanning_platform", ({ p_name, p_slug }) => {
});
});
socket.on("scan:scanning_rom", ({ p_slug, file_name, r_name }) => {
const platform = scannedPlatforms.value.find((p) => p.slug === p_slug);
platform.roms.push({
name: r_name,
file_name: file_name,
});
socket.on("scan:scanning_rom", ({ p_slug, p_name, ...rom }) => {
let platform = scannedPlatforms.value.find((p) => p.slug === p_slug);
// Add the platform if the socket dropped and it's missing
if (!platform) {
scannedPlatforms.value.push({
name: p_name,
slug: p_slug,
roms: [],
});
platform = scannedPlatforms.slice(-1);
}
platform.roms.push(rom);
});
socket.on("scan:done", () => {
@@ -52,6 +61,8 @@ socket.on("scan:done_ko", (msg) => {
socket.disconnect();
});
window.socket = socket;
// Functions
async function scan() {
scanning.set(true);
@@ -135,8 +146,8 @@ async function scan() {
</v-avatar>
<span class="text-body-2 ml-5"> {{ platform.name }}</span>
<v-list-item v-for="rom in platform.roms" class="text-body-2" disabled>
<span v-if="rom.name" class="ml-10">
Identified <b>{{ rom.name }} 👾</b>
<span v-if="rom.r_name" class="ml-10">
Identified <b>{{ rom.r_name }} 👾</b>
</span>
<span v-else class="ml-10">
{{ rom.file_name }} not found in IGDB

134
poetry.lock generated
View File

@@ -1,10 +1,27 @@
# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
[[package]]
name = "aioredis"
version = "2.0.1"
description = "asyncio (PEP 3156) Redis support"
optional = false
python-versions = ">=3.6"
files = [
{file = "aioredis-2.0.1-py3-none-any.whl", hash = "sha256:9ac0d0b3b485d293b8ca1987e6de8658d7dafcca1cddfcd1d506cae8cdebfdd6"},
{file = "aioredis-2.0.1.tar.gz", hash = "sha256:eaa51aaf993f2d71f54b70527c440437ba65340588afeb786cd87c55c89cd98e"},
]
[package.dependencies]
async-timeout = "*"
typing-extensions = "*"
[package.extras]
hiredis = ["hiredis (>=1.0)"]
[[package]]
name = "alembic"
version = "1.10.4"
description = "A database migration tool for SQLAlchemy."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -24,7 +41,6 @@ tz = ["python-dateutil"]
name = "anyio"
version = "3.6.2"
description = "High level compatibility layer for multiple asynchronous event loop implementations"
category = "main"
optional = false
python-versions = ">=3.6.2"
files = [
@@ -45,7 +61,6 @@ trio = ["trio (>=0.16,<0.22)"]
name = "appnope"
version = "0.1.3"
description = "Disable App Nap on macOS >= 10.9"
category = "main"
optional = false
python-versions = "*"
files = [
@@ -57,7 +72,6 @@ files = [
name = "asttokens"
version = "2.2.1"
description = "Annotate AST trees with source code positions"
category = "main"
optional = false
python-versions = "*"
files = [
@@ -71,11 +85,21 @@ six = "*"
[package.extras]
test = ["astroid", "pytest"]
[[package]]
name = "async-timeout"
version = "4.0.2"
description = "Timeout context manager for asyncio programs"
optional = false
python-versions = ">=3.6"
files = [
{file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"},
{file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
]
[[package]]
name = "backcall"
version = "0.2.0"
description = "Specifications for callback functions passed in to an API"
category = "main"
optional = false
python-versions = "*"
files = [
@@ -87,7 +111,6 @@ files = [
name = "bidict"
version = "0.22.1"
description = "The bidirectional mapping library for Python."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -104,7 +127,6 @@ test = ["hypothesis", "pytest", "pytest-benchmark[histogram]", "pytest-cov", "py
name = "certifi"
version = "2023.7.22"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@@ -116,7 +138,6 @@ files = [
name = "charset-normalizer"
version = "3.1.0"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "main"
optional = false
python-versions = ">=3.7.0"
files = [
@@ -201,7 +222,6 @@ files = [
name = "click"
version = "8.1.3"
description = "Composable command line interface toolkit"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -216,7 +236,6 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""}
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
@@ -228,7 +247,6 @@ files = [
name = "decorator"
version = "5.1.1"
description = "Decorators for Humans"
category = "main"
optional = false
python-versions = ">=3.5"
files = [
@@ -240,7 +258,6 @@ files = [
name = "emoji"
version = "2.2.0"
description = "Emoji for Python"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
@@ -254,7 +271,6 @@ dev = ["coverage", "coveralls", "pytest"]
name = "exceptiongroup"
version = "1.1.2"
description = "Backport of PEP 654 (exception groups)"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -269,7 +285,6 @@ test = ["pytest (>=6)"]
name = "executing"
version = "1.2.0"
description = "Get the currently executing AST node of a frame, and other information"
category = "main"
optional = false
python-versions = "*"
files = [
@@ -284,7 +299,6 @@ tests = ["asttokens", "littleutils", "pytest", "rich"]
name = "fastapi"
version = "0.95.1"
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -306,7 +320,6 @@ test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==23.1.0)", "coverage[toml] (>=6
name = "fastapi-pagination"
version = "0.12.4"
description = "FastAPI pagination"
category = "main"
optional = false
python-versions = ">=3.8,<4.0"
files = [
@@ -339,7 +352,6 @@ tortoise = ["tortoise-orm (>=0.16.18,<0.20.0)"]
name = "greenlet"
version = "2.0.2"
description = "Lightweight in-process concurrent programming"
category = "main"
optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
files = [
@@ -413,7 +425,6 @@ test = ["objgraph", "psutil"]
name = "h11"
version = "0.14.0"
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -425,7 +436,6 @@ files = [
name = "idna"
version = "3.4"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "main"
optional = false
python-versions = ">=3.5"
files = [
@@ -437,7 +447,6 @@ files = [
name = "iniconfig"
version = "2.0.0"
description = "brain-dead simple config-ini parsing"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -449,7 +458,6 @@ files = [
name = "ipdb"
version = "0.13.13"
description = "IPython-enabled pdb"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
@@ -466,7 +474,6 @@ tomli = {version = "*", markers = "python_version > \"3.6\" and python_version <
name = "ipython"
version = "8.13.2"
description = "IPython: Productive Interactive Computing"
category = "main"
optional = false
python-versions = ">=3.9"
files = [
@@ -505,7 +512,6 @@ test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pa
name = "jedi"
version = "0.18.2"
description = "An autocompletion tool for Python that can be used for text editors."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@@ -525,7 +531,6 @@ testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"]
name = "mako"
version = "1.2.4"
description = "A super-fast templating language that borrows the best ideas from the existing templating languages."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -545,7 +550,6 @@ testing = ["pytest"]
name = "mariadb"
version = "1.1.7"
description = "Python MariaDB extension"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@@ -567,7 +571,6 @@ packaging = "*"
name = "markupsafe"
version = "2.1.2"
description = "Safely add untrusted strings to HTML/XML markup."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -627,7 +630,6 @@ files = [
name = "matplotlib-inline"
version = "0.1.6"
description = "Inline Matplotlib backend for Jupyter"
category = "main"
optional = false
python-versions = ">=3.5"
files = [
@@ -642,7 +644,6 @@ traitlets = "*"
name = "multidict"
version = "6.0.4"
description = "multidict implementation"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -726,7 +727,6 @@ files = [
name = "packaging"
version = "23.1"
description = "Core utilities for Python packages"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -738,7 +738,6 @@ files = [
name = "parso"
version = "0.8.3"
description = "A Python Parser"
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@@ -754,7 +753,6 @@ testing = ["docopt", "pytest (<6.0.0)"]
name = "pexpect"
version = "4.8.0"
description = "Pexpect allows easy control of interactive console applications."
category = "main"
optional = false
python-versions = "*"
files = [
@@ -769,7 +767,6 @@ ptyprocess = ">=0.5"
name = "pickleshare"
version = "0.7.5"
description = "Tiny 'shelve'-like database with concurrency support"
category = "main"
optional = false
python-versions = "*"
files = [
@@ -781,7 +778,6 @@ files = [
name = "pluggy"
version = "1.2.0"
description = "plugin and hook calling mechanisms for python"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -797,7 +793,6 @@ testing = ["pytest", "pytest-benchmark"]
name = "prompt-toolkit"
version = "3.0.38"
description = "Library for building powerful interactive command lines in Python"
category = "main"
optional = false
python-versions = ">=3.7.0"
files = [
@@ -812,7 +807,6 @@ wcwidth = "*"
name = "ptyprocess"
version = "0.7.0"
description = "Run a subprocess in a pseudo terminal"
category = "main"
optional = false
python-versions = "*"
files = [
@@ -824,7 +818,6 @@ files = [
name = "pure-eval"
version = "0.2.2"
description = "Safely evaluate AST nodes without side effects"
category = "main"
optional = false
python-versions = "*"
files = [
@@ -839,7 +832,6 @@ tests = ["pytest"]
name = "pydantic"
version = "1.10.7"
description = "Data validation and settings management using python type hints"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -892,7 +884,6 @@ email = ["email-validator (>=1.0.3)"]
name = "pydash"
version = "7.0.6"
description = "The kitchen sink of Python utility libraries for doing \"stuff\" in a functional way. Based on the Lo-Dash Javascript library."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -910,7 +901,6 @@ dev = ["Sphinx", "black", "build", "coverage", "docformatter", "flake8", "flake8
name = "pygments"
version = "2.15.1"
description = "Pygments is a syntax highlighting package written in Python."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -925,7 +915,6 @@ plugins = ["importlib-metadata"]
name = "pytest"
version = "7.4.0"
description = "pytest: simple powerful testing with Python"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -948,7 +937,6 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no
name = "pytest-env"
version = "0.8.2"
description = "py.test plugin that allows you to add environment variables."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -966,7 +954,6 @@ test = ["coverage (>=7.2.7)", "pytest-mock (>=3.10)"]
name = "pytest-mock"
version = "3.11.1"
description = "Thin-wrapper around the mock package for easier use with pytest"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -984,7 +971,6 @@ dev = ["pre-commit", "pytest-asyncio", "tox"]
name = "pytest-vcr"
version = "1.0.2"
description = "Plugin for managing VCR.py cassettes"
category = "main"
optional = false
python-versions = "*"
files = [
@@ -1000,7 +986,6 @@ vcrpy = "*"
name = "python-dateutil"
version = "2.8.2"
description = "Extensions to the standard Python datetime module"
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
files = [
@@ -1015,7 +1000,6 @@ six = ">=1.5"
name = "python-dotenv"
version = "1.0.0"
description = "Read key-value pairs from a .env file and set them as environment variables"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@@ -1030,7 +1014,6 @@ cli = ["click (>=5.0)"]
name = "python-engineio"
version = "4.4.1"
description = "Engine.IO server and client for Python"
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@@ -1046,7 +1029,6 @@ client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"]
name = "python-socketio"
version = "5.8.0"
description = "Socket.IO server and client for Python"
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@@ -1066,7 +1048,6 @@ client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"]
name = "pyyaml"
version = "6.0"
description = "YAML parser and emitter for Python"
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@@ -1112,11 +1093,28 @@ files = [
{file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
]
[[package]]
name = "redis"
version = "4.6.0"
description = "Python client for Redis database and key-value store"
optional = false
python-versions = ">=3.7"
files = [
{file = "redis-4.6.0-py3-none-any.whl", hash = "sha256:e2b03db868160ee4591de3cb90d40ebb50a90dd302138775937f6a42b7ed183c"},
{file = "redis-4.6.0.tar.gz", hash = "sha256:585dc516b9eb042a619ef0a39c3d7d55fe81bdb4df09a52c9cdde0d07bf1aa7d"},
]
[package.dependencies]
async-timeout = {version = ">=4.0.2", markers = "python_full_version <= \"3.11.2\""}
[package.extras]
hiredis = ["hiredis (>=1.0.0)"]
ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"]
[[package]]
name = "requests"
version = "2.31.0"
description = "Python HTTP for Humans."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -1134,11 +1132,25 @@ urllib3 = ">=1.21.1,<3"
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "rq"
version = "1.15.1"
description = "RQ is a simple, lightweight, library for creating background jobs, and processing them."
optional = false
python-versions = ">=3.6"
files = [
{file = "rq-1.15.1-py2.py3-none-any.whl", hash = "sha256:6e243d8d9c4af4686ded4b01b25ea1ff4bac4fc260b02638fbe9c8c17b004bd1"},
{file = "rq-1.15.1.tar.gz", hash = "sha256:1f49f4ac1a084044bb8e95b3f305c0bf17e55618b08c18e0b60c080f12d6f008"},
]
[package.dependencies]
click = ">=5.0.0"
redis = ">=4.0.0"
[[package]]
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
files = [
@@ -1150,7 +1162,6 @@ files = [
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"
files = [
@@ -1162,7 +1173,6 @@ files = [
name = "sqlakeyset"
version = "2.0.1684285512"
description = "offset-free paging for sqlalchemy"
category = "main"
optional = false
python-versions = ">=3.7,<4.0"
files = [
@@ -1180,7 +1190,6 @@ typing_extensions = ">=4,<5"
name = "sqlalchemy"
version = "2.0.12"
description = "Database Abstraction Library"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -1228,7 +1237,7 @@ files = [
]
[package.dependencies]
greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""}
greenlet = {version = "!=0.4.17", markers = "platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\""}
typing-extensions = ">=4.2.0"
[package.extras]
@@ -1258,7 +1267,6 @@ sqlcipher = ["sqlcipher3-binary"]
name = "stack-data"
version = "0.6.2"
description = "Extract data from python stack frames and tracebacks for informative displays"
category = "main"
optional = false
python-versions = "*"
files = [
@@ -1278,7 +1286,6 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"]
name = "starlette"
version = "0.26.1"
description = "The little ASGI library that shines."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -1296,7 +1303,6 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyam
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -1308,7 +1314,6 @@ files = [
name = "traitlets"
version = "5.9.0"
description = "Traitlets Python configuration system"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -1324,7 +1329,6 @@ test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"]
name = "typing-extensions"
version = "4.5.0"
description = "Backported and Experimental Type Hints for Python 3.7+"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -1336,7 +1340,6 @@ files = [
name = "unidecode"
version = "1.3.6"
description = "ASCII transliterations of Unicode text"
category = "main"
optional = false
python-versions = ">=3.5"
files = [
@@ -1348,7 +1351,6 @@ files = [
name = "urllib3"
version = "2.0.2"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -1366,7 +1368,6 @@ zstd = ["zstandard (>=0.18.0)"]
name = "uvicorn"
version = "0.22.0"
description = "The lightning-fast ASGI server."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -1385,7 +1386,6 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)",
name = "vcrpy"
version = "5.1.0"
description = "Automatically mock your HTTP interactions to simplify and speed up testing"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@@ -1402,7 +1402,6 @@ yarl = "*"
name = "wcwidth"
version = "0.2.6"
description = "Measures the displayed width of unicode strings in a terminal"
category = "main"
optional = false
python-versions = "*"
files = [
@@ -1414,7 +1413,6 @@ files = [
name = "websockets"
version = "11.0.3"
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -1494,7 +1492,6 @@ files = [
name = "wrapt"
version = "1.15.0"
description = "Module for decorators, wrappers and monkey patching."
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
files = [
@@ -1579,7 +1576,6 @@ files = [
name = "yarl"
version = "1.9.2"
description = "Yet another URL library"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@@ -1666,4 +1662,4 @@ multidict = ">=4.0"
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
content-hash = "226d69976d98caf0de533c83cd447c2e1074234f104ecedc33eb3b61d50a58a9"
content-hash = "9f86d5d57b6df96b54973786c7129f7a6bb2182466558463a8336ffe0cc5c0f5"

View File

@@ -27,6 +27,8 @@ pytest-env = "^0.8.2"
pytest-vcr = "^1.0.2"
pytest-mock = "^3.11.1"
mariadb = "1.1.7"
rq = "^1.15.1"
aioredis = "^2.0.1"
[build-system]