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

Refactor type annotations and documentation #37

Merged
merged 10 commits into from
Apr 18, 2022
8 changes: 8 additions & 0 deletions docs/ext/menus/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ GroupByPageSource
:members:
:inherited-members:

GroupByEntry
>>>>>>>>>>>>

.. attributetable:: GroupByEntry

.. autoclass:: GroupByEntry
:members:

AsyncIteratorPageSource
~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
2 changes: 1 addition & 1 deletion docs/ext/menus/reaction_menus.rst
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,6 @@ For the sake of example, here’s a basic list source that is paginated:
await pages.start(ctx)

The :meth:`PageSource.format_page` can return either a :class:`str` for content,
:class:`nextcord.Embed` for an embed, :class:`List[nextcord.Embed]` for
:class:`nextcord.Embed` for an embed, List[:class:`nextcord.Embed`] for
sending multiple embeds, or a :class:`dict` to pass into the kwargs
of :meth:`nextcord.Message.edit`.
4 changes: 2 additions & 2 deletions nextcord/ext/menus/constants.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import logging
from typing import Dict, List, Union
from typing import Any, Dict, List, Union

import nextcord

Expand All @@ -12,7 +12,7 @@

# type definition for the keyword-arguments that are
# used in both Message.edit and Messageable.send
SendKwargsType = Dict[str, Union[str, nextcord.Embed, nextcord.ui.View, None]]
SendKwargsType = Dict[str, Any]

# type definition for possible page formats
PageFormatType = Union[str, nextcord.Embed, List[nextcord.Embed], SendKwargsType]
Expand Down
4 changes: 2 additions & 2 deletions nextcord/ext/menus/menu_pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ async def _get_kwargs_from_page(self, page: List[Any]) -> SendKwargsType:
--------
TypeError
The return value of :meth:`PageSource.format_page` was not a
:class:`str`, :class:`nextcord.Embed`, :class:`List[nextcord.Embed]`,
:class:`str`, :class:`nextcord.Embed`, List[:class:`nextcord.Embed`],
or :class:`dict`.
"""
value: PageFormatType = await nextcord.utils.maybe_coroutine(
Expand Down Expand Up @@ -342,7 +342,7 @@ async def _get_kwargs_from_page(self, page: List[Any]) -> SendKwargsType:
--------
TypeError
The return value of :meth:`PageSource.format_page` was not a
:class:`str`, :class:`nextcord.Embed`, :class:`List[nextcord.Embed]`,
:class:`str`, :class:`nextcord.Embed`, List[:class:`nextcord.Embed`],
or :class:`dict`.
"""
kwargs = await super()._get_kwargs_from_page(page)
Expand Down
106 changes: 87 additions & 19 deletions nextcord/ext/menus/page_source.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
from collections import namedtuple
import inspect
import itertools
from typing import (
Any,
AsyncIterator,
Callable,
List,
NamedTuple,
Optional,
Sequence,
TypeVar,
Union,
)

from .constants import PageFormatType, SendKwargsType
from .constants import PageFormatType
from .menus import Menu

DataType = TypeVar("DataType")
Expand Down Expand Up @@ -123,7 +123,7 @@ async def format_page(self, menu: Menu, page: Any) -> PageFormatType:
as returning the ``embed`` keyword argument in :meth:`nextcord.Message.edit`
and :meth:`nextcord.abc.Messageable.send`.

If this method returns a :class:`List[nextcord.Embed]` then it is interpreted
If this method returns a List[:class:`nextcord.Embed`] then it is interpreted
as returning the ``embeds`` keyword argument in :meth:`nextcord.Message.edit`
and :meth:`nextcord.abc.Messageable.send`.

Expand All @@ -141,7 +141,7 @@ async def format_page(self, menu: Menu, page: Any) -> PageFormatType:

Returns
---------
Union[:class:`str`, :class:`nextcord.Embed`, :class:`dict`]
Union[:class:`str`, :class:`nextcord.Embed`, List[:class:`nextcord.Embed`], :class:`dict`]
See above.
"""
raise NotImplementedError
Expand Down Expand Up @@ -197,15 +197,51 @@ async def get_page(self, page_number: int) -> Union[DataType, List[DataType]]:
base = page_number * self.per_page
return self.entries[base : base + self.per_page]

async def format_page(
self, menu: Menu, page: Union[DataType, List[DataType]]
) -> PageFormatType:
"""An abstract method to format the page.

This works similar to the :meth:`PageSource.format_page` except
the type of the ``page`` parameter is documented.

Parameters
------------
menu: :class:`Menu`
The menu that wants to format this page.
page: Union[Any, List[Any]]
The page returned by :meth:`get_page`. This is either a single element
if :attr:`per_page` is set to ``1`` or a slice of the sequence otherwise.

Returns
---------
Union[:class:`str`, :class:`nextcord.Embed`, List[:class:`nextcord.Embed`], :class:`dict`]
See :meth:`PageSource.format_page`.
"""
raise NotImplementedError


KeyType = TypeVar("KeyType")

KeyFuncType = Callable[[DataType], KeyType]


class _GroupByEntry(NamedTuple):
class GroupByEntry(namedtuple("GroupByEntry", "key items")):
"""Named tuple representing an entry returned by
:meth:`GroupByPageSource.get_page` in a :class:`GroupByPageSource`.

Attributes
------------
key: Callable[[Any], Any]
A key of the :func:`itertools.groupby` function.
items: List[Any]
Slice of the paginated items within the group.
"""

__slots__ = ()

key: KeyFuncType
items: DataType
items: List[DataType]


class GroupByPageSource(ListPageSource):
Expand Down Expand Up @@ -245,7 +281,7 @@ def __init__(
sort: int = True
):
self.__entries = entries if not sort else sorted(entries, key=key)
nested: List[_GroupByEntry] = []
nested: List[GroupByEntry] = []
self.nested_per_page = per_page
for key_i, group_i in itertools.groupby(self.__entries, key=key):
group_i = list(group_i)
Expand All @@ -255,35 +291,44 @@ def __init__(

# Chunk the nested pages
nested.extend(
_GroupByEntry(key=key_i, items=group_i[i : i + per_page])
GroupByEntry(key=key_i, items=group_i[i : i + per_page])
for i in range(0, size, per_page)
)

super().__init__(nested, per_page=1)

async def get_page(self, page_number: int) -> DataType:
async def get_page(self, page_number: int) -> GroupByEntry:
"""Returns a :class:`GroupByEntry` with ``key``, representing the
key of the :func:`itertools.groupby` function, and ``items``,
representing a sequence of paginated items within that group.

Returns
---------
GroupByEntry
The data returned.
"""
return self.entries[page_number]

async def format_page(self, menu: Menu, entry: _GroupByEntry) -> SendKwargsType:
async def format_page(self, menu: Menu, entry: GroupByEntry) -> PageFormatType:
"""An abstract method to format the page.

This works similar to the :meth:`ListPageSource.format_page` except
the return type of the ``entry`` parameter is documented.
This works similar to the :meth:`PageSource.format_page` except
the type of the ``entry`` parameter is documented.

Parameters
------------
menu: :class:`Menu`
The menu that wants to format this page.
entry
A namedtuple with ``(key, items)`` representing the key of the
group by function and a sequence of paginated items within that
group.
entry: GroupByEntry
The page returned by :meth:`get_page`. This will be a
:class:`GroupByEntry` with ``key``, representing the key of the
:func:`itertools.groupby` function, and ``items``, representing
a sequence of paginated items within that group.

Returns
---------
:class:`dict`
A dictionary representing keyword-arguments to pass to
the message related calls.
Union[:class:`str`, :class:`nextcord.Embed`, List[:class:`nextcord.Embed`], :class:`dict`]
See :meth:`PageSource.format_page`.
"""
raise NotImplementedError

Expand Down Expand Up @@ -389,3 +434,26 @@ async def get_page(self, page_number: int) -> Union[DataType, List[DataType]]:
return await self._get_single_page(page_number)
else:
return await self._get_page_range(page_number)

async def format_page(
self, menu: Menu, page: Union[DataType, List[DataType]]
) -> PageFormatType:
"""An abstract method to format the page.

This works similar to the :meth:`PageSource.format_page` except
the type of the ``page`` parameter is documented.

Parameters
------------
menu: :class:`Menu`
The menu that wants to format this page.
page: Union[Any, List[Any]]
The page returned by :meth:`get_page`. This is either a single element
if :attr:`per_page` is set to ``1`` or a slice of the sequence otherwise.

Returns
---------
Union[:class:`str`, :class:`nextcord.Embed`, List[:class:`nextcord.Embed`], :class:`dict`]
See :meth:`PageSource.format_page`.
"""
raise NotImplementedError