From 060bad105dc2569fc485adb03b985aa2ab5d367e Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Tue, 22 Feb 2022 23:47:09 +0000 Subject: Move new utilities to the util namespace --- botcore/utils/extensions.py | 52 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 botcore/utils/extensions.py (limited to 'botcore/utils/extensions.py') diff --git a/botcore/utils/extensions.py b/botcore/utils/extensions.py new file mode 100644 index 00000000..c8f200ad --- /dev/null +++ b/botcore/utils/extensions.py @@ -0,0 +1,52 @@ +"""Utilities for loading discord extensions.""" + +import importlib +import inspect +import pkgutil +import types +from typing import NoReturn + + +def unqualify(name: str) -> str: + """ + Return an unqualified name given a qualified module/package `name`. + + Args: + name: The module name to unqualify. + + Returns: + The unqualified module name. + """ + return name.rsplit(".", maxsplit=1)[-1] + + +def walk_extensions(module: types.ModuleType) -> frozenset[str]: + """ + Yield extension names from the bot.exts subpackage. + + Args: + module (types.ModuleType): The module to look for extensions in. + + Returns: + A set of strings that can be passed directly to :obj:`discord.ext.commands.Bot.load_extension`. + """ + + def on_error(name: str) -> NoReturn: + raise ImportError(name=name) # pragma: no cover + + modules = set() + + for module_info in pkgutil.walk_packages(module.__path__, f"{module.__name__}.", onerror=on_error): + if unqualify(module_info.name).startswith("_"): + # Ignore module/package names starting with an underscore. + continue + + if module_info.ispkg: + imported = importlib.import_module(module_info.name) + if not inspect.isfunction(getattr(imported, "setup", None)): + # If it lacks a setup function, it's not an extension. + continue + + modules.add(module_info.name) + + return frozenset(modules) -- cgit v1.2.3 From aa2f9685c29d46a2666654c545d4461763c903b6 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Wed, 23 Feb 2022 00:59:09 +0000 Subject: Alter docstrings to look better in autodocs --- botcore/utils/channel.py | 13 +++++++++++-- botcore/utils/extensions.py | 4 ++-- botcore/utils/loggers.py | 10 +++++----- botcore/utils/members.py | 14 +++++++++++--- botcore/utils/scheduling.py | 2 +- 5 files changed, 30 insertions(+), 13 deletions(-) (limited to 'botcore/utils/extensions.py') diff --git a/botcore/utils/channel.py b/botcore/utils/channel.py index 7e0fc387..6a630c94 100644 --- a/botcore/utils/channel.py +++ b/botcore/utils/channel.py @@ -1,4 +1,4 @@ -"""Utilities for interacting with discord channels.""" +"""Useful helper functions for interacting with various discord.py channel objects.""" import discord from discord.ext.commands import Bot @@ -9,7 +9,16 @@ log = loggers.get_logger(__name__) def is_in_category(channel: discord.TextChannel, category_id: int) -> bool: - """Return True if `channel` is within a category with `category_id`.""" + """ + Return whether the given channel has the category_id. + + Args: + channel: The channel to check. + category_id: The category to check for. + + Returns: + A bool depending on whether the channel is in the category. + """ return getattr(channel, "category_id", None) == category_id diff --git a/botcore/utils/extensions.py b/botcore/utils/extensions.py index c8f200ad..3f8d6e6d 100644 --- a/botcore/utils/extensions.py +++ b/botcore/utils/extensions.py @@ -9,7 +9,7 @@ from typing import NoReturn def unqualify(name: str) -> str: """ - Return an unqualified name given a qualified module/package `name`. + Return an unqualified name given a qualified module/package ``name``. Args: name: The module name to unqualify. @@ -22,7 +22,7 @@ def unqualify(name: str) -> str: def walk_extensions(module: types.ModuleType) -> frozenset[str]: """ - Yield extension names from the bot.exts subpackage. + Yield extension names from the given module. Args: module (types.ModuleType): The module to look for extensions in. diff --git a/botcore/utils/loggers.py b/botcore/utils/loggers.py index ac1db920..740c20d4 100644 --- a/botcore/utils/loggers.py +++ b/botcore/utils/loggers.py @@ -1,4 +1,4 @@ -"""Custom logging class.""" +"""Custom :obj:`logging.Logger` class that implements a new ``"TRACE"`` level.""" import logging import typing @@ -12,11 +12,11 @@ TRACE_LEVEL = 5 class CustomLogger(LoggerClass): - """Custom implementation of the `Logger` class with an added `trace` method.""" + """Custom implementation of the :obj:`logging.Logger` class with an added :obj:`trace` method.""" def trace(self, msg: str, *args, **kwargs) -> None: """ - Log 'msg % args' with severity 'TRACE'. + Log the given message with the severity ``"TRACE"``. To pass exception information, use the keyword argument exc_info with a true value: @@ -34,12 +34,12 @@ class CustomLogger(LoggerClass): def get_logger(name: typing.Optional[str] = None) -> CustomLogger: """ - Utility to make mypy recognise that logger is of type `CustomLogger`. + Utility to make mypy recognise that logger is of type :obj:`CustomLogger`. Args: name: The name given to the logger. Returns: - An instance of the `CustomLogger` class. + An instance of the :obj:`CustomLogger` class. """ return typing.cast(CustomLogger, logging.getLogger(name)) diff --git a/botcore/utils/members.py b/botcore/utils/members.py index abe7e5e1..e7b31342 100644 --- a/botcore/utils/members.py +++ b/botcore/utils/members.py @@ -1,3 +1,5 @@ +"""Useful helper functions for interactin with :obj:`discord.Member` objects.""" + import typing import discord @@ -11,7 +13,8 @@ async def get_or_fetch_member(guild: discord.Guild, member_id: int) -> typing.Op """ Attempt to get a member from cache; on failure fetch from the API. - Return `None` to indicate the member could not be found. + Returns: + The :obj:`discord.Member` or :obj:`None` to indicate the member could not be found. """ if member := guild.get_member(member_id): log.trace(f"{member} retrieved from cache.") @@ -31,9 +34,14 @@ async def handle_role_change( role: discord.Role ) -> None: """ - Change `member`'s cooldown role via awaiting `coro` and handle errors. + Await the given ``coro`` with ``member`` as the sole argument. + + Handle errors that we expect to be raised from + :obj:`discord.Member.add_roles` and :obj:`discord.Member.remove_roles`. - `coro` is intended to be `discord.Member.add_roles` or `discord.Member.remove_roles`. + Args: + member: The member to pass to ``coro``. + coro: This is intended to be :obj:`discord.Member.add_roles` or :obj:`discord.Member.remove_roles`. """ try: await coro(role) diff --git a/botcore/utils/scheduling.py b/botcore/utils/scheduling.py index 947df0d9..d6969302 100644 --- a/botcore/utils/scheduling.py +++ b/botcore/utils/scheduling.py @@ -215,7 +215,7 @@ def create_task( **kwargs, ) -> asyncio.Task: """ - Wrapper for creating asyncio `Tasks` which logs exceptions raised in the task. + Wrapper for creating an :obj:`asyncio.Task` which logs exceptions raised in the task. If the loop kwarg is provided, the task is created from that event loop, otherwise the running loop is used. -- cgit v1.2.3