Custom Protocols ⚙️¶
Dementor is not limited to a predefined set of protocols and can be extended with custom implementations. Each protocol extension is defined as a Python module that implements specific functions recognized by the core engine.
Protocol Module Structure¶
Changed in version 1.0.0.dev21.
Dementor now uses a class-based system for protocol extensions. A custom protocol is implemented as a
subclass of dementor.loader.BaseProtocolModule and may define the following class
attributes (all optional unless noted):
Attribute |
Description |
Required? |
|---|---|---|
|
Human readable protocol name (e.g. |
Yes |
|
|
No (if configuration is handled manually) |
|
Name of the attribute on |
No (defaults to |
|
Name of the boolean flag on the session that enables the protocol. |
No |
|
|
No (defaults to |
|
|
No (defaults to |
|
Concrete server class used to instantiate threads for each configuration entry. If omitted, the protocol must implement |
No |
In addition to the attributes, a protocol class can optionally override the following methods:
- Protocol.apply_config(self, session: SessionConfig) None¶
Called during session setup to populate configuration objects. The default implementation reads
config_tyand stores the result underconfig_attr(handling list vs single config).
- Protocol.create_server_thread(self, session: SessionConfig, server_config: _ConfigTy) BaseServerThread¶
Create a single server thread for the given configuration. The default implementation uses
server_tyif provided.
- Protocol.create_server_threads(self, session: SessionConfig) List[BaseServerThread]¶
Returns all server threads for the protocol. The base implementation respects
config_enabled_attrandconfig_list.
Legacy function-based extensions (defining apply_config) are still supported but will only
use the apply_config function.
Module discovery
Every custom protocol module must expose a top-level __proto__ attribute. This attribute should be a list containing either the protocol class objects or their class names (as strings). The loader uses __proto__ to discover which protocol classes to instantiate.
__proto__ = ["POP3Protocol"]
The __proto__ entry is required for the loader to register the protocol; otherwise the module will be ignored.
To enable a custom protocol, its Python module must be placed in a directory listed under the
Dementor.ExtraModules setting in your configuration file, the protocols package
within Dementor or within the protocols directory under ~/.dementor/protocols.
Example: POP3 Protocol Extension (class-based)¶
The following example shows the POP3 protocol implementation using the new class-based API.
from threading import Thread
from dementor.loader import BaseProtocolModule
from dementor.config import TomlConfig, Attribute as A
from dementor.servers import ServerThread
__proto__ = ["POP3Protocol"]
# Configuration schema for POP3
class POP3Config(TomlConfig):
_section_ = "POP3"
_fields_ = [
A("pop3_port", "Port", 110),
]
class POP3Server(ThreadingTCPServer):
def __init__(
self,
config,
server_address=None,
RequestHandlerClass: type | None = None,
server_config: POP3ServerConfig | None = None,
) -> None:
...
class POP3Protocol(BaseProtocolModule):
"""Protocol implementation for POP3.
The ``BaseProtocolModule`` base class handles configuration loading and
thread creation based on the attributes defined below.
"""
name = "POP3"
config_ty = POP3Config
config_attr = "<DEFAULT>" # results in ``pop3_config`` on the session
config_enabled_attr = "<DEFAULT>" # results in ``pop3_enabled`` flag
config_list = True # enabled multiple servers
# No need to override ``apply_config``
@override
def create_server_thread(
self, session: SessionConfig, server_config: POP3ServerConfig
) -> BaseServerThread:
return ServerThread(
session,
server_config,
POP3Server,
server_address=(session.bind_address, server_config.pop3_port),
include_server_config=True, # required, because it is specified in the server's __init__
)