diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 8177a91..01e25c1 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,4 @@
-image: python:3.9-alpine
+image: python:3.10-alpine
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
diff --git a/litecord/blueprints/channel/messages.py b/litecord/blueprints/channel/messages.py
index ee1b6ba..c1a621b 100644
--- a/litecord/blueprints/channel/messages.py
+++ b/litecord/blueprints/channel/messages.py
@@ -29,7 +29,8 @@ from litecord.errors import MessageNotFound, Forbidden
from litecord.enums import MessageType, ChannelType, GUILD_CHANS
from litecord.schemas import validate, MESSAGE_CREATE
-from litecord.utils import pg_set_json, query_tuple_from_args, extract_limit
+from litecord.utils import query_tuple_from_args, extract_limit
+from litecord.json import pg_set_json
from litecord.permissions import get_permissions
from litecord.embed.sanitizer import fill_embed
diff --git a/litecord/blueprints/webhooks.py b/litecord/blueprints/webhooks.py
index 9c903da..cc5d4ab 100644
--- a/litecord/blueprints/webhooks.py
+++ b/litecord/blueprints/webhooks.py
@@ -52,7 +52,7 @@ from litecord.common.messages import (
from litecord.embed.sanitizer import fill_embed, fetch_mediaproxy_img
from litecord.embed.messages import process_url_embed, is_media_url
from litecord.embed.schemas import EmbedURL
-from litecord.utils import pg_set_json
+from litecord.json import pg_set_json
from litecord.enums import MessageType
from litecord.images import STATIC_IMAGE_MIMES
diff --git a/litecord/gateway/encoding.py b/litecord/gateway/encoding.py
index a2001d8..dc187b3 100644
--- a/litecord/gateway/encoding.py
+++ b/litecord/gateway/encoding.py
@@ -20,7 +20,7 @@ along with this program. If not, see .
import json
import earl
-from litecord.utils import LitecordJSONEncoder
+from litecord.json import LitecordJSONEncoder
def encode_json(payload) -> str:
diff --git a/litecord/json.py b/litecord/json.py
new file mode 100644
index 0000000..e6fccbf
--- /dev/null
+++ b/litecord/json.py
@@ -0,0 +1,69 @@
+"""
+
+Litecord
+Copyright (C) 2018-2021 Luna Mendes and Litecord Contributors
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 3 of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+"""
+
+import json
+from typing import Any
+from decimal import Decimal
+from uuid import UUID
+from dataclasses import asdict, is_dataclass
+
+import quart.json.provider
+
+
+class LitecordJSONEncoder(json.JSONEncoder):
+ """Custom JSON encoder for Litecord. Useful for json.dumps"""
+
+ def default(self, value: Any):
+ if isinstance(value, (Decimal, UUID)):
+ return str(value)
+
+ if is_dataclass(value):
+ return asdict(value)
+
+ if hasattr(value, "to_json"):
+ return value.to_json
+
+ return super().default(self, value)
+
+
+class LitecordJSONProvider(quart.json.provider.DefaultJSONProvider):
+ """Custom JSON provider for Quart."""
+
+ def __init__(self, *args, **kwargs):
+ self.encoder = LitecordJSONEncoder(**kwargs)
+
+ def default(self, value: Any):
+ self.encoder.default(value)
+
+
+async def pg_set_json(con):
+ """Set JSON and JSONB codecs for an asyncpg connection."""
+ await con.set_type_codec(
+ "json",
+ encoder=lambda v: json.dumps(v, cls=LitecordJSONEncoder),
+ decoder=json.loads,
+ schema="pg_catalog",
+ )
+
+ await con.set_type_codec(
+ "jsonb",
+ encoder=lambda v: json.dumps(v, cls=LitecordJSONEncoder),
+ decoder=json.loads,
+ schema="pg_catalog",
+ )
diff --git a/litecord/storage.py b/litecord/storage.py
index a4cec8b..3f9b77d 100644
--- a/litecord/storage.py
+++ b/litecord/storage.py
@@ -33,7 +33,7 @@ from litecord.blueprints.channel.reactions import (
from litecord.blueprints.user.billing import PLAN_ID_TO_TYPE
from litecord.types import timestamp_
-from litecord.utils import pg_set_json
+from litecord.json import pg_set_json
log = Logger(__name__)
diff --git a/litecord/utils.py b/litecord/utils.py
index 028f7e6..f153d85 100644
--- a/litecord/utils.py
+++ b/litecord/utils.py
@@ -18,14 +18,12 @@ along with this program. If not, see .
"""
import asyncio
-import json
import secrets
import datetime
import re
from typing import Any, Iterable, Optional, Sequence, List, Dict, Union
from logbook import Logger
-from quart.json import JSONEncoder
from quart import current_app as app
from litecord.common.messages import message_view
@@ -156,35 +154,6 @@ def mmh3(inp_str: str, seed: int = 0):
return _u(h1) >> 0
-class LitecordJSONEncoder(JSONEncoder):
- """Custom JSON encoder for Litecord."""
-
- def default(self, value: Any):
- """By default, this will try to get the to_json attribute of a given
- value being JSON encoded."""
- try:
- return value.to_json
- except AttributeError:
- return super().default(value)
-
-
-async def pg_set_json(con):
- """Set JSON and JSONB codecs for an asyncpg connection."""
- await con.set_type_codec(
- "json",
- encoder=lambda v: json.dumps(v, cls=LitecordJSONEncoder),
- decoder=json.loads,
- schema="pg_catalog",
- )
-
- await con.set_type_codec(
- "jsonb",
- encoder=lambda v: json.dumps(v, cls=LitecordJSONEncoder),
- decoder=json.loads,
- schema="pg_catalog",
- )
-
-
def yield_chunks(input_list: Sequence[Any], chunk_size: int):
"""Yield successive n-sized chunks from l.
diff --git a/pyproject.toml b/pyproject.toml
index 31ce245..4f996c0 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -7,19 +7,19 @@ license = "GPLv3-only"
[tool.poetry.dependencies]
python = "^3.9"
-bcrypt = "^3.2.0"
-itsdangerous = "^1.1.0"
-asyncpg = "^0.24.0"
-websockets = "^10.0"
+bcrypt = "^3.2.2"
+itsdangerous = "^2.1.2"
+asyncpg = "^0.26.0"
+websockets = "^10.3"
Earl-ETF = "^2.1.2"
logbook = "^1.5.3"
Cerberus = "^1.3.4"
-quart = {git = "https://gitlab.com/pgjones/quart", rev = "c1ac142c6c51709765045f830b242950099b2295"}
-pillow = "^8.3.2"
-aiohttp = "^3.7.4"
-zstandard = "^0.15.2"
+quart = "^0.18.0"
+pillow = "^9.2.0"
+aiohttp = "^3.8.1"
+zstandard = "^0.18.0"
winter = {git = "https://gitlab.com/elixire/winter"}
-wsproto = "^1.0.0"
+wsproto = "^1.1.0"
diff --git a/run.py b/run.py
index df6fb03..0f22d69 100644
--- a/run.py
+++ b/run.py
@@ -105,7 +105,7 @@ from litecord.pubsub.lazy_guild import LazyGuildManager
from litecord.gateway.gateway import websocket_handler
-from litecord.utils import LitecordJSONEncoder
+from litecord.json import LitecordJSONProvider
# == HACKY PATCH ==
# this MUST be removed once Hypercorn gets py3.10 support.
@@ -135,7 +135,7 @@ def make_app():
logging.getLogger("websockets").setLevel(logbook.INFO)
# use our custom json encoder for custom data types
- app.json_encoder = LitecordJSONEncoder
+ app.json_provider_class = LitecordJSONProvider
return app
diff --git a/tox.ini b/tox.ini
index ed53a2f..43fde9a 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,22 +1,22 @@
[tox]
-envlist = py3.9
+envlist = py3.10
isolated_build = true
[testenv]
ignore_errors = true
deps =
- pytest==6.2.5
- pytest-asyncio==0.15.1
- pytest-cov==2.12.1
- flake8==3.9.2
- black==21.6b0
- mypy==0.910
+ pytest==7.1.2
+ pytest-asyncio==0.19.0
+ pytest-cov==3.0.0
+ flake8==5.0.4
+ black==22.6.0
+ mypy==0.971
pytest-instafail==0.4.2
commands =
python3 ./manage.py migrate
black --check litecord run.py tests manage
flake8 litecord run.py tests manage
- pytest {posargs:tests}
+ pytest --asyncio-mode=auto {posargs:tests}
[flake8]
max-line-length = 88