mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-29 22:44:19 +00:00
Document and improve some parts of Royalnet code
This commit is contained in:
parent
81c8fb6fa7
commit
bb1f7b0795
11 changed files with 99 additions and 33 deletions
|
@ -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
15
royalnet/__init__.py
Normal 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.
|
||||||
|
"""
|
|
@ -1,3 +1,7 @@
|
||||||
|
"""
|
||||||
|
This submodule implements quick alias for some common SQL filtering functions.
|
||||||
|
"""
|
||||||
|
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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."""
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
"""
|
||||||
|
|
Loading…
Reference in a new issue