hikari-py/hikari

Allow a single builder with `components=`

Closed this issue · 2 comments

Summary

Allow users to pass a custom class that is built into a list of action row payloads. This should help with abstracting some custom component handlers.

Why is this needed?

There have been a few new component handlers recently. Generally, users are forced to use components as follows:

handler = library.SomeHandler(...)
await channel.send(components=handler.build())

It'd be ideal if the .build() could be abstracted away with:

handler = library.SomeHandler(...)
await channel.send(components=handler)

Ideal implementations

new builder protocol/abc

components will allow Sequence[ComponentBuilder] | CoolComponentHandlerNameIsWIP

await channel.send(components=handler)

allow pre-existing ComponentBuilder to return lists

If ComponentBuilder returns a sequence it will be assumed to be a list of action row payloads.

await channel.send(component=handler)

Checklist

  • I have searched the issue tracker and have made sure it's not a duplicate. If it is a follow up of another issue, I have specified it.

I dont think this will be implemented, mostly because the solution to remove the .build() is quite simple. All you need is to make your handler = library.SomeHandler(...) a sequence, as specified in the typehint. This would look something like this:

class SomeComponent:
    def to_builder() -> ComponentBuilder:
        return ComponentBuilder(...)

class SomeHandler(typing.Sequence[ComponentBuilder]):
    _custom_components: typing.List[SomeComponent]

    def __len__(self) -> int:
        return len(self._custom_components)

    @typing.overload
    def __getitem__(self, index: int) -> ComponentBuilder:
        ...

    @typing.overload
    def __getitem__(self, index: slice) -> typing.Sequence[ComponentBuilder]:
        ...

    def __getitem__(
        self, index: typing.Union[int, slice]
    ) -> typing.Union[ComponentBuilder, typing.Sequence[ComponentBuilder]]:
        if isinstance(index, int):
            return self._custom_components[index].to_builder()

        return [component.to_builder() for component in self._custom_components[index]]

    def __iter__(self) -> typing.Iterator[ComponentBuilder]:
        # This is not necessary, but a worthwhile optimization
        yield from (component.to_builder() for component in self._custom_components)

and that can be safely passed into the components argument.

Because of this, will be closing this issue. Feel free to comment to it or open a new one if you have any comments :)

Ye, outdated. Would still be cool to allow for just an iterable though.