diff --git a/backend/endpoints/__init__.py b/backend/endpoints/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/backend/endpoints/scan.py b/backend/endpoints/scan.py
index aa8e6f9da..1c96796f1 100644
--- a/backend/endpoints/scan.py
+++ b/backend/endpoints/scan.py
@@ -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)
diff --git a/backend/handler/socket_manager.py b/backend/handler/socket_manager.py
index cf1bcdbaa..71d2193d8 100644
--- a/backend/handler/socket_manager.py
+++ b/backend/handler/socket_manager.py
@@ -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)
diff --git a/backend/main.py b/backend/main.py
index 94b506780..e1fe9818a 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -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")
diff --git a/docker-compose.yml b/docker-compose.yml
index f17c8b0c1..077af13c9 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -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
diff --git a/env.template b/env.template
index 17e16876a..b30c8ae02 100644
--- a/env.template
+++ b/env.template
@@ -18,3 +18,6 @@ DB_NAME=romm
DB_USER=romm
DB_PASSWD=
DB_ROOT_PASSWD=
+
+# Redis config (development only)
+REDIS_PORT=6379
diff --git a/frontend/src/views/library/Scan.vue b/frontend/src/views/library/Scan.vue
index 5b15fbcf9..18e0d85da 100644
--- a/frontend/src/views/library/Scan.vue
+++ b/frontend/src/views/library/Scan.vue
@@ -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() {
{{ platform.name }}
-
- • Identified {{ rom.name }} 👾
+
+ • Identified {{ rom.r_name }} 👾
• {{ rom.file_name }} not found in IGDB
diff --git a/poetry.lock b/poetry.lock
index 9f34e424c..6517df6de 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -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"
diff --git a/pyproject.toml b/pyproject.toml
index eeea9bea0..9a8411e35 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -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]