Skip to content

Commit

Permalink
bot, plugins: fix incomplete type defs
Browse files Browse the repository at this point in the history
  • Loading branch information
Exirel committed Oct 14, 2024
1 parent 52ad026 commit b786a59
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 58 deletions.
63 changes: 33 additions & 30 deletions sopel/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
from types import MappingProxyType
from typing import (
Any,
Callable,
Optional,
Sequence,
TYPE_CHECKING,
TypeVar,
Union,
Expand All @@ -36,6 +38,8 @@

if TYPE_CHECKING:
from collections.abc import Iterable, Mapping

from sopel.plugins.handlers import AbstractPluginHandler
from sopel.trigger import PreTrigger


Expand Down Expand Up @@ -182,7 +186,6 @@ def hostmask(self) -> Optional[str]:
:return: the bot's current hostmask if the bot is connected and in
a least one channel; ``None`` otherwise
:rtype: Optional[str]
"""
if not self.users or self.nick not in self.users:
# bot must be connected and in at least one channel
Expand All @@ -198,11 +201,11 @@ def plugins(self) -> Mapping[str, plugins.handlers.AbstractPluginHandler]:
"""
return MappingProxyType(self._plugins)

def has_channel_privilege(self, channel, privilege) -> bool:
def has_channel_privilege(self, channel: str, privilege: int) -> bool:
"""Tell if the bot has a ``privilege`` level or above in a ``channel``.
:param str channel: a channel the bot is in
:param int privilege: privilege level to check
:param channel: a channel the bot is in
:param privilege: privilege level to check
:raise ValueError: when the channel is unknown
This method checks the bot's privilege level in a channel, i.e. if it
Expand Down Expand Up @@ -339,10 +342,10 @@ def post_setup(self) -> None:

# plugins management

def reload_plugin(self, name) -> None:
def reload_plugin(self, name: str) -> None:
"""Reload a plugin.
:param str name: name of the plugin to reload
:param name: name of the plugin to reload
:raise plugins.exceptions.PluginNotRegistered: when there is no
``name`` plugin registered
Expand Down Expand Up @@ -391,45 +394,49 @@ def reload_plugins(self) -> None:

# TODO: deprecate both add_plugin and remove_plugin; see #2425

def add_plugin(self, plugin, callables, jobs, shutdowns, urls) -> None:
def add_plugin(
self,
plugin: AbstractPluginHandler,
callables: Sequence[Callable],
jobs: Sequence[Callable],
shutdowns: Sequence[Callable],
urls: Sequence[Callable],
) -> None:
"""Add a loaded plugin to the bot's registry.
:param plugin: loaded plugin to add
:type plugin: :class:`sopel.plugins.handlers.AbstractPluginHandler`
:param callables: an iterable of callables from the ``plugin``
:type callables: :term:`iterable`
:param jobs: an iterable of functions from the ``plugin`` that are
periodically invoked
:type jobs: :term:`iterable`
:param shutdowns: an iterable of functions from the ``plugin`` that
should be called on shutdown
:type shutdowns: :term:`iterable`
:param urls: an iterable of functions from the ``plugin`` to call when
matched against a URL
:type urls: :term:`iterable`
"""
self._plugins[plugin.name] = plugin
self.register_callables(callables)
self.register_jobs(jobs)
self.register_shutdowns(shutdowns)
self.register_urls(urls)

def remove_plugin(self, plugin, callables, jobs, shutdowns, urls) -> None:
def remove_plugin(
self,
plugin: AbstractPluginHandler,
callables: Sequence[Callable],
jobs: Sequence[Callable],
shutdowns: Sequence[Callable],
urls: Sequence[Callable],
) -> None:
"""Remove a loaded plugin from the bot's registry.
:param plugin: loaded plugin to remove
:type plugin: :class:`sopel.plugins.handlers.AbstractPluginHandler`
:param callables: an iterable of callables from the ``plugin``
:type callables: :term:`iterable`
:param jobs: an iterable of functions from the ``plugin`` that are
periodically invoked
:type jobs: :term:`iterable`
:param shutdowns: an iterable of functions from the ``plugin`` that
should be called on shutdown
:type shutdowns: :term:`iterable`
:param urls: an iterable of functions from the ``plugin`` to call when
matched against a URL
:type urls: :term:`iterable`
"""
name = plugin.name
if not self.has_plugin(name):
Expand Down Expand Up @@ -993,12 +1000,11 @@ def on_scheduler_error(
self,
scheduler: plugin_jobs.Scheduler,
exc: BaseException,
):
) -> None:
"""Called when the Job Scheduler fails.
:param scheduler: the job scheduler that errored
:type scheduler: :class:`sopel.plugins.jobs.Scheduler`
:param Exception exc: the raised exception
:param exc: the raised exception
.. seealso::
Expand All @@ -1011,14 +1017,12 @@ def on_job_error(
scheduler: plugin_jobs.Scheduler,
job: tools_jobs.Job,
exc: BaseException,
):
) -> None:
"""Called when a job from the Job Scheduler fails.
:param scheduler: the job scheduler responsible for the errored ``job``
:type scheduler: :class:`sopel.plugins.jobs.Scheduler`
:param job: the Job that errored
:type job: :class:`sopel.tools.jobs.Job`
:param Exception exc: the raised exception
:param exc: the raised exception
.. seealso::
Expand All @@ -1030,12 +1034,11 @@ def error(
self,
trigger: Optional[Trigger] = None,
exception: Optional[BaseException] = None,
):
) -> None:
"""Called internally when a plugin causes an error.
:param trigger: the ``Trigger``\\ing line (if available)
:type trigger: :class:`sopel.trigger.Trigger`
:param Exception exception: the exception raised by the error (if
:param exception: the exception raised by the error (if
available)
"""
message = 'Unexpected error'
Expand All @@ -1056,7 +1059,7 @@ def error(
def _host_blocked(self, host: str) -> bool:
"""Check if a hostname is blocked.
:param str host: the hostname to check
:param host: the hostname to check
"""
bad_masks = self.config.core.host_blocks
for bad_mask in bad_masks:
Expand All @@ -1071,7 +1074,7 @@ def _host_blocked(self, host: str) -> bool:
def _nick_blocked(self, nick: str) -> bool:
"""Check if a nickname is blocked.
:param str nick: the nickname to check
:param nick: the nickname to check
"""
bad_nicks = self.config.core.nick_blocks
for bad_nick in bad_nicks:
Expand Down
7 changes: 7 additions & 0 deletions sopel/plugins/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ class AbstractPluginHandler(abc.ABC):
on shutdown (either upon exiting Sopel or unloading that plugin).
"""

name: str
"""Plugin identifier.
The name of a plugin identifies this plugin: when Sopel loads a plugin,
it will store its information under that identifier.
"""

@abc.abstractmethod
def load(self):
"""Load the plugin.
Expand Down
46 changes: 18 additions & 28 deletions sopel/plugins/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import threading
from typing import (
Any,
Callable,
Optional,
Type,
TYPE_CHECKING,
Expand All @@ -39,7 +40,11 @@

if TYPE_CHECKING:
from collections.abc import Generator, Iterable

from sopel.bot import Sopel
from sopel.config import Config
from sopel.tools.identifiers import Identifier
from sopel.trigger import PreTrigger


__all__ = [
Expand Down Expand Up @@ -541,14 +546,16 @@ class AbstractRule(abc.ABC):
"""
@classmethod
@abc.abstractmethod
def from_callable(cls: Type[TypedRule], settings, handler) -> TypedRule:
def from_callable(
cls: Type[TypedRule],
settings: Config,
handler: Callable,
) -> TypedRule:
"""Instantiate a rule object from ``settings`` and ``handler``.
:param settings: Sopel's settings
:type settings: :class:`sopel.config.Config`
:param callable handler: a function-based rule handler
:param handler: a function-based rule handler
:return: an instance of this class created from the ``handler``
:rtype: :class:`AbstractRule`
Sopel's function-based rule handlers are simple callables, decorated
with :mod:`sopel.plugin`'s decorators to add attributes, such as rate
Expand Down Expand Up @@ -580,8 +587,6 @@ def priority_scale(self):
def get_plugin_name(self) -> str:
"""Get the rule's plugin name.
:rtype: str
The rule's plugin name will be used in various places to select,
register, unregister, and manipulate the rule based on its plugin,
which is referenced by its name.
Expand All @@ -591,8 +596,6 @@ def get_plugin_name(self) -> str:
def get_rule_label(self) -> str:
"""Get the rule's label.
:rtype: str
A rule can have a label, which can identify the rule by string, the
same way a plugin can be identified by its name. This label can be used
to select, register, unregister, and manipulate the rule based on its
Expand All @@ -603,8 +606,6 @@ def get_rule_label(self) -> str:
def get_usages(self) -> tuple:
"""Get the rule's usage examples.
:rtype: tuple
A rule can have usage examples, i.e. a list of examples showing how
the rule can be used, or in what context it can be triggered.
"""
Expand All @@ -613,8 +614,6 @@ def get_usages(self) -> tuple:
def get_test_parameters(self) -> tuple:
"""Get parameters for automated tests.
:rtype: tuple
A rule can have automated tests attached to it, and this method must
return the test parameters:
Expand All @@ -633,8 +632,6 @@ def get_test_parameters(self) -> tuple:
def get_doc(self) -> str:
"""Get the rule's documentation.
:rtype: str
A rule's documentation is a short text that can be displayed to a user
on IRC upon asking for help about this rule. The equivalent of Python
docstrings, but for IRC rules.
Expand All @@ -644,8 +641,6 @@ def get_doc(self) -> str:
def get_priority(self) -> str:
"""Get the rule's priority.
:rtype: str
A rule can have a priority, based on the three pre-defined priorities
used by Sopel: ``PRIORITY_HIGH``, ``PRIORITY_MEDIUM``, and
``PRIORITY_LOW``.
Expand All @@ -662,35 +657,30 @@ def get_priority(self) -> str:
def get_output_prefix(self) -> str:
"""Get the rule's output prefix.
:rtype: str
.. seealso::
See the :class:`sopel.bot.SopelWrapper` class for more information
on how the output prefix can be used.
"""

@abc.abstractmethod
def match(self, bot, pretrigger) -> Iterable:
def match(self, bot: Sopel, pretrigger: PreTrigger) -> Iterable:
"""Match a pretrigger according to the rule.
:param bot: Sopel instance
:type bot: :class:`sopel.bot.Sopel`
:param pretrigger: line to match
:type pretrigger: :class:`sopel.trigger.PreTrigger`
This method must return a list of `match objects`__.
.. __: https://docs.python.org/3.11/library/re.html#match-objects
"""

@abc.abstractmethod
def match_event(self, event) -> bool:
def match_event(self, event: str) -> bool:
"""Tell if the rule matches this ``event``.
:param str event: potential matching event
:param event: potential matching event
:return: ``True`` when ``event`` matches the rule, ``False`` otherwise
:rtype: bool
"""

@abc.abstractmethod
Expand Down Expand Up @@ -845,7 +835,7 @@ def global_rate_template(self) -> Optional[str]:
"""

@abc.abstractmethod
def parse(self, text) -> Generator:
def parse(self, text: str) -> Generator:
"""Parse ``text`` and yield matches.
:param str text: text to parse by the rule
Expand Down Expand Up @@ -1046,7 +1036,7 @@ def __init__(self,
self._handler = handler

# filters
self._events = events or ['PRIVMSG']
self._events: list[str] = events or ['PRIVMSG']
self._ctcp = ctcp or []
self._allow_bots = bool(allow_bots)
self._allow_echo = bool(allow_echo)
Expand Down Expand Up @@ -1171,10 +1161,10 @@ def parse(self, text):
if result:
yield result

def match_event(self, event) -> bool:
def match_event(self, event: str | None) -> bool:
return bool(event and event in self._events)

def match_ctcp(self, command: Optional[str]) -> bool:
def match_ctcp(self, command: str | None) -> bool:
if not self._ctcp:
return True

Expand Down

0 comments on commit b786a59

Please sign in to comment.