1
Fork 0
mirror of https://github.com/RYGhub/royalnet.git synced 2024-11-26 21:14:19 +00:00

Document and improve some parts of Royalnet code

This commit is contained in:
Steffo 2022-02-27 00:42:47 +01:00
parent 81c8fb6fa7
commit bb1f7b0795
11 changed files with 99 additions and 33 deletions

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "royalnet" name = "royalnet"
version = "6.5.6" version = "6.6.0"
description = "A multipurpose bot framework" description = "A multipurpose bot framework"
authors = ["Stefano Pigozzi <me@steffo.eu>"] authors = ["Stefano Pigozzi <me@steffo.eu>"]
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"

15
royalnet/__init__.py Normal file
View file

@ -0,0 +1,15 @@
"""
Royalnet is a collection of many Python modules useful to build chat-like applications.
The modules currently are:
- :mod:`.alchemist`, containing utility extensions to :mod:`sqlalchemy`;
- :mod:`.engineer`, containing a framework for building chat bots;
- :mod:`.lazy`, containing utilities to delay the evaluation of values until they are actually used;
- :mod:`.scrolls`, containing configuration utilities;
- :mod:`.sculptor`, containing common :mod:`pydantic` models for serialization and deserialization of data structures;
To prevent the library from breaking if optional dependencies are not installed, this module does not export any object; submodules should be directly imported instead.
All modules use exceptions based on :exc:`.exc.RoyalnetException`, and may subclass it to provide more detail on the errors.
"""

View file

@ -1,3 +1,7 @@
"""
This submodule implements quick alias for some common SQL filtering functions.
"""
import sqlalchemy import sqlalchemy

View file

@ -1,3 +1,7 @@
"""
This submodule implements the :class:`.Makeable` mixin.
"""
from royalnet.royaltyping import * from royalnet.royaltyping import *
import sqlalchemy.orm as o import sqlalchemy.orm as o

View file

@ -1,3 +1,8 @@
"""
This submodule implements :func:`repr`\\ esentation mixins.
"""
class ColRepr: class ColRepr:
""" """
A mixin that can be added to a declared class to display all columns of the table with their values in the A mixin that can be added to a declared class to display all columns of the table with their values in the

View file

@ -1,3 +1,7 @@
"""
This module implements the :class:`.Updatable` mixin, along with the special class :class:`.DoNotUpdateType` and the singleton :data:`.DoNotUpdate`.
"""
class Updatable: class Updatable:
""" """
A mixin that can be added to a declared class to add update methods, allowing attributes to be set from A mixin that can be added to a declared class to add update methods, allowing attributes to be set from

View file

@ -4,6 +4,7 @@ This module contains the base :class:`.PDAImplementation` and its basic implemen
""" """
import royalnet.royaltyping as t import royalnet.royaltyping as t
import royalnet.exc as exc
import abc import abc
import sys import sys
import asyncio import asyncio
@ -21,28 +22,28 @@ DispenserKey = t.Hashable
class PDAImplementation(metaclass=abc.ABCMeta): class PDAImplementation(metaclass=abc.ABCMeta):
""" """
.. todo:: Document this. An abstract class describing the interface of a PDA implementation.
""" """
def __init__(self, name: str): def __init__(self, name: str):
self.name: str = f"{self.namespace}.{name}" self.name: str = f"{self.namespace}.{name}"
""" """
.. todo:: Document this. The namespaced name of the PDA implementation.
""" """
self.bound_to: t.Optional["PDA"] = None self.bound_to: t.Optional["PDA"] = None
""" """
.. todo:: Document this. The PDA this implementation is bound to.
""" """
self.logger_name: str = f"{__name__}.PDAImplementation.{self.namespace}.{name}" self.logger_name: str = f"{__name__}.PDAImplementation.{self.namespace}.{name}"
""" """
.. todo:: Document this. The namespaced name of the :class:`logging.Logger` that is being used by this PDA implementation.
""" """
self.log: logging.Logger = logging.getLogger(self.logger_name) self.log: logging.Logger = logging.getLogger(self.logger_name)
""" """
.. todo:: Document this. The :class:`logging.Logger` that is being used by this PDA implementation.
""" """
def __repr__(self): def __repr__(self):
@ -53,7 +54,11 @@ class PDAImplementation(metaclass=abc.ABCMeta):
def bind(self, pda: "PDA") -> None: def bind(self, pda: "PDA") -> None:
""" """
.. todo:: Document this. Bind this PDA implementation to a specific PDA.
Required for objects of this class to function.
:raises .ImplemetationAlreadyBoundError: if the PDA implementation is already bound to a PDA.
""" """
self.log.debug(f"Trying to bind to {pda!r}...") self.log.debug(f"Trying to bind to {pda!r}...")
@ -66,9 +71,9 @@ class PDAImplementation(metaclass=abc.ABCMeta):
@property @property
@abc.abstractmethod @abc.abstractmethod
def namespace(self): def namespace(self) -> str:
""" """
.. todo:: Document this. The namespace of this PDA implementation.
""" """
raise NotImplementedError() raise NotImplementedError()
@ -82,15 +87,15 @@ class PDAImplementation(metaclass=abc.ABCMeta):
raise NotImplementedError() raise NotImplementedError()
class ImplementationException(Exception): class ImplementationException(exc.RoyalnetException, metaclass=abc.ABCMeta):
""" """
.. todo:: Document this. Base class for exceptions raised by a PDA implementation.
""" """
class ImplementationAlreadyBoundError(ImplementationException): class ImplementationAlreadyBoundError(ImplementationException):
""" """
.. todo:: Document this. The PDA implementation is already bound to a PDA.
""" """

View file

@ -14,21 +14,33 @@ log = logging.getLogger(__name__)
class Router(c.Conversation, metaclass=abc.ABCMeta): class Router(c.Conversation, metaclass=abc.ABCMeta):
""" """
.. todo:: Document this. A conversation which delegates event handling to other conversations by matching the contents of the first message received to one or multiple regexes.
""" """
def __init__(self): def __init__(self):
"""
.. todo:: Document this.
"""
self.by_pattern: dict[t.Pattern, t.ConversationProtocol] = {} self.by_pattern: dict[t.Pattern, t.ConversationProtocol] = {}
self.by_name: dict[str, t.ConversationProtocol] = {}
self.by_command: dict[t.ConversationProtocol, t.List[str]] = {}
def register_conversation(self, conv: t.ConversationProtocol, names: t.List[str], patterns: t.List[t.Pattern]):
""" """
.. todo:: Document this. A :class:`dict` mapping regex patterns to conversations registered with this router.
"""
self.by_name: dict[str, t.ConversationProtocol] = {}
"""
A :class:`dict` mapping command names to conversations registered with this router.
"""
self.by_command: dict[t.ConversationProtocol, t.List[str]] = {}
"""
A :class:`dict` mapping conversations registered with this router to lists of command names.
"""
self.else_convs: list[t.ConversationProtocol] = []
"""
A :class:`list` of conversations to delegate event handling to in case no other pattern is matched.
"""
def register_conversation(self, conv: t.ConversationProtocol, names: t.List[str], patterns: t.List[t.Pattern]) -> None:
"""
Registers a new conversation with the :class:`.Router`, allowing it to run if one of the specified ``patterns`` is matched.
""" """
log.debug(f"Registering {conv!r}...") log.debug(f"Registering {conv!r}...")
@ -45,10 +57,6 @@ class Router(c.Conversation, metaclass=abc.ABCMeta):
self.by_pattern[pattern] = conv self.by_pattern[pattern] = conv
async def run(self, _sentry: s.Sentry, _conv: t.ConversationProtocol, **kwargs) -> None: async def run(self, _sentry: s.Sentry, _conv: t.ConversationProtocol, **kwargs) -> None:
"""
.. todo:: Document this.
"""
dispenser = _sentry.dispenser() dispenser = _sentry.dispenser()
log.debug(f"Locking {dispenser!r}...") log.debug(f"Locking {dispenser!r}...")
@ -93,8 +101,16 @@ class Router(c.Conversation, metaclass=abc.ABCMeta):
) )
return return
else: else:
log.debug("No matches found") for conversation in self.else_convs:
log.debug(f"No matches found, running conversation {conversation}")
await conversation(
**kwargs,
_sentry=_sentry,
_conv=conversation,
_msg=msg,
_text=text,
_router=self,
)
__all__ = ( __all__ = (
"Router", "Router",

View file

@ -1,7 +1,6 @@
""" """
This module contains the :class:`.Sentry` class and its descendents :class:`SentryFilter` and :class:`SentrySource`\\ . This module contains the :class:`.Sentry` class and its descendents :class:`SentryFilter` and :class:`SentrySource`\\ .
They support event filtering through Wrenches and coroutine functions. They support event filtering through Wrenches and coroutine functions.
""" """
@ -113,7 +112,7 @@ class Sentry(metaclass=abc.ABCMeta):
if callable(wrench): if callable(wrench):
return SentryFilter(previous=self, wrench=wrench) return SentryFilter(previous=self, wrench=wrench)
else: else:
raise TypeError("wrench must be either a Wrench or a coroutine function") raise TypeError("wrench parameter must be either a Wrench or a coroutine function")
def __or__(self, other: t.WrenchLike) -> SentryFilter: def __or__(self, other: t.WrenchLike) -> SentryFilter:
""" """
@ -130,7 +129,7 @@ class Sentry(metaclass=abc.ABCMeta):
try: try:
return self.filter(other) return self.filter(other)
except TypeError: except TypeError:
raise TypeError("Right-side must be either a Wrench or a coroutine function") raise TypeError("Right-side of bitwise-or operator must be either a Wrench or a coroutine function")
@abc.abstractmethod @abc.abstractmethod
def dispenser(self) -> Dispenser: def dispenser(self) -> Dispenser:

View file

@ -1,3 +1,8 @@
"""
This module exports the base exceptions used in all :mod:`royalnet` modules.
"""
class RoyalnetException(Exception): class RoyalnetException(Exception):
"""An exception raised by a Royalnet module.""" """An exception raised by a Royalnet module."""

View file

@ -1,9 +1,11 @@
""" """
This module adds some new common royaltyping to the default typing package. This module defines adds some common types to the default :mod:`typing` module present in the standard library.
It should be imported with: :: It is recommended to import it with *one* of the following statements::
import royalnet.royaltyping as t
from royalnet.royaltyping import * from royalnet.royaltyping import *
from royalnet.royaltyping import <used_objects>
""" """
@ -45,4 +47,11 @@ class ConversationProtocol(Protocol):
Args = Collection[Any] Args = Collection[Any]
"""
Any possible combination of positional arguments.
"""
Kwargs = Mapping[str, Any] Kwargs = Mapping[str, Any]
"""
Any possible combination of keyword arguments.
"""