Skip to content

Commit

Permalink
refactor: Refactor type annotations and documentation (#37)
Browse files Browse the repository at this point in the history
* Fix List type documentation

* Document ListPageSource.format_page differences

* Change SendKwargsType to Dict[str,Any] and _GroupByEntry to GroupByEntry

* Bring back SendKwargsType

* Fix GroupByEntry initialization

* Change GroupByEntry back to namedtuple

* Document AsyncIteratorPageSource.format_page differences

* Add GroupByEntry type to entry doc in format_page

* Remove unused import

* Annotate attribute types of GroupByEntry
  • Loading branch information
DenverCoder1 authored Apr 18, 2022
1 parent 430a15e commit 21131e3
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 24 deletions.
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

0 comments on commit 21131e3

Please sign in to comment.