hikari-py / hikari

A Discord API wrapper for Python and asyncio built on good intentions.
https://docs.hikari-py.dev/en/stable
MIT License
806 stars 95 forks source link

Passing localization dicts with `hikari.Locale` keys fails #1955

Closed hypergonial closed 1 month ago

hypergonial commented 3 months ago

Steps to reproduce

  1. Try to pass a dictionary that has type dict[hikari.Locale, str] to any object's constructor (all command & option builders seem to be affected) that has localizations.
  2. Try to push the object to Discord (sync command)
  3. Observe bug

Expected result

The command should be synced normally.

Actual result

Traceback (most recent call last):
  File "/home/hyper/Code/Python/miru-testbot/arc_random.py", line 17, in on_startup
    await bot.rest.set_application_commands(app, [command])
  File "/home/hyper/.local/lib/python3.12/site-packages/hikari/impl/rest.py", line 3867, in set_application_commands
    response = await self._request(route, json=[command.build(self._entity_factory) for command in commands])
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/hyper/.local/lib/python3.12/site-packages/hikari/impl/rest.py", line 733, in _request
    await aio.first_completed(request_task, self._close_event.wait())
  File "/home/hyper/.local/lib/python3.12/site-packages/hikari/internal/aio.py", line 135, in first_completed
    await next(iterator)
  File "/usr/lib/python3.12/asyncio/tasks.py", line 631, in _wait_for_one
    return f.result()  # May raise f.exception().
           ^^^^^^^^^^
  File "/home/hyper/.local/lib/python3.12/site-packages/hikari/impl/rest.py", line 780, in _perform_request
    data = data_binding.JSONPayload(json, dumps=self._dumps)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/hyper/.local/lib/python3.12/site-packages/hikari/internal/data_binding.py", line 129, in __init__
    super().__init__(dumps(value), content_type=_JSON_CONTENT_TYPE, encoding=_UTF_8)
                     ^^^^^^^^^^^^
TypeError: Dict key must be str

This error goes away if the localization dict's type is dict[str, str], but the typehint for the hikari parameter (t.Mapping[str | hikari.Locale, str] suggests that hikari.Locale is a supported key type.

System info

hikari (2.0.0.dev126) [f65737f4]
located at /home/hyper/.local/lib/python3.12/site-packages/hikari
CPython 3.12.4 GCC 14.1.1 20240522
Linux archlinux 6.9.7-zen1-1-zen #1 ZEN SMP PREEMPT_DYNAMIC Fri, 28 Jun 2024 04:32:27 +0000 x86_64

Further info

Minimal example that reproduces the error:

import hikari

bot = hikari.GatewayBot("TOKEN_HERE")

@bot.listen()
async def on_startup(event: hikari.StartedEvent) -> None:
    command = hikari.impl.SlashCommandBuilder(
        name="hi",
        description="Say hi to the bot",
        name_localizations={hikari.Locale.EN_US: "hi", hikari.Locale.ES_ES: "hola"},
    )
    app = await bot.rest.fetch_application()
    await bot.rest.set_application_commands(app, [command])
    print("Command registered!")

@bot.listen()
async def on_inter(event: hikari.InteractionCreateEvent) -> None:
    if not isinstance(event.interaction, hikari.CommandInteraction):
        return

    if event.interaction.command_name == "hi":
        await event.interaction.create_initial_response(
            hikari.ResponseType.MESSAGE_CREATE, "Hello!"
        )

bot.run()

This example type-checks on pyright strict at the time of writing.

Checklist

davfsa commented 3 months ago

After further investigating, issue seems to stem because of how orjson serializes payloads.

Using json works fine.

Think there can be quite a lot of fixes here, so I'll investigate into which is the best.