Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add orjson as an optional speedup and allow to pass custom json.dumps and json.loads functions to all components. #1486

Merged
merged 22 commits into from
Feb 5, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ other internal settings in the interpreter.

If you have a C compiler (Microsoft VC++ Redistributable 14.0 or newer, or a modern copy of GCC/G++, Clang, etc), it is
recommended you install Hikari using `pip install -U hikari[speedups]`. This will install `aiohttp` with its available
speedups, and `ciso8601` which will provide you with a small performance boost.
speedups, `ciso8601` and `orjson` which will provide you with a substantial performance boost.

### `uvloop`

Expand Down
1 change: 1 addition & 0 deletions changes/1486.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `orjson` as an optional speedup and allow to pass custom `json.dumps` and `json.loads` functions to all components.
4 changes: 3 additions & 1 deletion hikari/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
)

import http
import json
import typing

import attr
Expand Down Expand Up @@ -317,7 +318,8 @@ def __str__(self) -> str:
try:
value += _dump_errors(self.errors).strip("\n")
except KeyError:
value += data_binding.dump_json(self.errors, indent=2)
# Use the stdlib json.dumps here to be able to indent
value += json.dumps(self.errors, indent=2)
davfsa marked this conversation as resolved.
Show resolved Hide resolved

self._cached_str = value
return value
Expand Down
15 changes: 15 additions & 0 deletions hikari/impl/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
from hikari.impl import shard as shard_impl
from hikari.impl import voice as voice_impl
from hikari.internal import aio
from hikari.internal import data_binding
from hikari.internal import signals
from hikari.internal import time
from hikari.internal import ux
Expand Down Expand Up @@ -237,6 +238,10 @@ class GatewayBot(traits.GatewayBotAware):
proxy_settings : typing.Optional[hikari.impl.config.ProxySettings]
Custom proxy settings to use with network-layer logic
in your application to get through an HTTP-proxy.
dumps : hikari.internal.data_binding.JSONEncoder
The JSON encoder this application should use. Defaults to `hikari.internal.data_binding.default_json_dumps`.
loads : hikari.internal.data_binding.JSONDecoder
The JSON decoder this application should use. Defaults to `hikari.internal.data_binding.default_json_loads`.
rest_url : typing.Optional[str]
Defaults to the Discord REST API URL if `None`. Can be
overridden if you are attempting to point to an unofficial endpoint, or
Expand Down Expand Up @@ -291,6 +296,8 @@ class GatewayBot(traits.GatewayBotAware):
"_shards",
"_token",
"_voice",
"_loads",
"_dumps",
"shards",
)

Expand All @@ -305,6 +312,8 @@ def __init__(
force_color: bool = False,
cache_settings: typing.Optional[config_impl.CacheSettings] = None,
http_settings: typing.Optional[config_impl.HTTPSettings] = None,
dumps: data_binding.JSONEncoder = data_binding.default_json_dumps,
loads: data_binding.JSONDecoder = data_binding.default_json_loads,
intents: intents_.Intents = intents_.Intents.ALL_UNPRIVILEGED,
auto_chunk_members: bool = True,
logs: typing.Union[None, int, str, typing.Dict[str, typing.Any]] = "INFO",
Expand All @@ -326,6 +335,8 @@ def __init__(
self._intents = intents
self._proxy_settings = proxy_settings if proxy_settings is not None else config_impl.ProxySettings()
self._token = token.strip()
self._dumps = dumps
self._loads = loads

# Caching
cache_settings = cache_settings if cache_settings is not None else config_impl.CacheSettings()
Expand Down Expand Up @@ -357,6 +368,8 @@ def __init__(
http_settings=self._http_settings,
max_rate_limit=max_rate_limit,
proxy_settings=self._proxy_settings,
dumps=dumps,
loads=loads,
rest_url=rest_url,
max_retries=max_retries,
token=token,
Expand Down Expand Up @@ -1252,6 +1265,8 @@ async def _start_one_shard(
event_manager=self._event_manager,
event_factory=self._event_factory,
intents=self._intents,
dumps=self._dumps,
loads=self._loads,
initial_activity=activity,
initial_is_afk=afk,
initial_idle_since=idle_since,
Expand Down
23 changes: 12 additions & 11 deletions hikari/impl/interaction_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def status_code(self) -> int:

# Constant response
_PONG_RESPONSE: typing.Final[_Response] = _Response(
_OK_STATUS, data_binding.dump_json({"type": _PONG_RESPONSE_TYPE}).encode(), content_type=_JSON_CONTENT_TYPE
_OK_STATUS, data_binding.dump_json({"type": _PONG_RESPONSE_TYPE}, encode=True), content_type=_JSON_CONTENT_TYPE
)


Expand Down Expand Up @@ -193,10 +193,10 @@ class InteractionServer(interaction_server.InteractionServer):

Other Parameters
----------------
dumps : aiohttp.typedefs.JSONEncoder
The JSON encoder this server should use. Defaults to `json.dumps`.
loads : aiohttp.typedefs.JSONDecoder
The JSON decoder this server should use. Defaults to `json.loads`.
dumps : hikari.internal.data_binding.JSONEncoder
The JSON encoder this server should use. Defaults to `hikari.internal.data_binding.default_json_dumps`.
loads : hikari.internal.data_binding.JSONDecoder
The JSON decoder this server should use. Defaults to `hikari.internal.data_binding.default_json_loads`.
public_key : bytes
The public key this server should use for verifying request payloads from
Discord. If left as `None` then the client will try to work this
Expand Down Expand Up @@ -224,10 +224,10 @@ class InteractionServer(interaction_server.InteractionServer):
def __init__(
self,
*,
dumps: aiohttp.typedefs.JSONEncoder = data_binding.dump_json,
dumps: data_binding.JSONEncoder = data_binding.default_json_dumps,
entity_factory: entity_factory_api.EntityFactory,
executor: typing.Optional[concurrent.futures.Executor] = None,
loads: aiohttp.typedefs.JSONDecoder = data_binding.load_json,
loads: data_binding.JSONDecoder = data_binding.default_json_loads,
rest_client: rest_api.RESTClient,
public_key: typing.Optional[bytes] = None,
) -> None:
Expand Down Expand Up @@ -433,10 +433,11 @@ async def on_interaction(self, body: bytes, signature: bytes, timestamp: bytes)
return _Response(_BAD_REQUEST_STATUS, b"Invalid request signature")

try:
payload = self._loads(body.decode("utf-8"))
payload = self._loads(body)
assert isinstance(payload, dict)
interaction_type = int(payload["type"])

except (data_binding.JSONDecodeError, ValueError, TypeError) as exc:
except (ValueError, TypeError) as exc:
_LOGGER.error("Received a request with an invalid JSON body", exc_info=exc)
return _Response(_BAD_REQUEST_STATUS, b"Invalid JSON body")

Expand Down Expand Up @@ -476,15 +477,15 @@ async def on_interaction(self, body: bytes, signature: bytes, timestamp: bytes)
result = await call

raw_payload, files = result.build(self._entity_factory)
payload = self._dumps(raw_payload)
payload = data_binding.dump_json(raw_payload, encode=True, json_dumps=self._dumps)

except Exception as exc:
asyncio.get_running_loop().call_exception_handler(
{"message": "Exception occurred during interaction dispatch", "exception": exc}
)
return _Response(_INTERNAL_SERVER_ERROR_STATUS, b"Exception occurred during interaction dispatch")

return _Response(_OK_STATUS, payload.encode(), files=files, content_type=_JSON_CONTENT_TYPE)
return _Response(_OK_STATUS, payload, files=files, content_type=_JSON_CONTENT_TYPE)

_LOGGER.debug(
"Ignoring interaction %s of type %s without registered listener", interaction.id, interaction.type
Expand Down
Loading