Writing Extensions
Writing Sending Methods
A sending method is implemented as a callable (usually a class) that accepts
the fields of a configuration structure as keyword arguments and returns a
sender object. The keyword arguments include the
method field and also include a configpath key specifying a
pathlib.Path pointing to the configuration file (or None if from_dict()
was called without setting a configpath). Callables should accept any
keyword argument and ignore any that they do not recognize.
For example, given the following configuration:
[outgoing]
method = "foobar"
server = "www.example.nil"
password = { env = "SECRET_TOKEN" }
comment = "I like e-mail!"
the callable registered for the “foobar” method will be called with the following keyword arguments:
**{
"method": "foobar",
"server": "www.example.nil",
"password": {"env": "SECRET_TOKEN"},
"comment": "I like e-mail!",
"configpath": Path("path/to/configfile"),
}
If the configuration passed to a callable is invalid, the callable should raise
an InvalidConfigError.
Callables can resolve password fields by passing them to resolve_password()
or by using pydantic and the Password type. Callables should resolve paths
relative to the directory containing configpath by using resolve_path()
or by using pydantic and the Path, FilePath, and/or DirectoryPath types.
The last step of writing a sending method is to package it in a Python project
and declare the callable as an entry point in the outgoing.senders entry
point group so that users can install & access it. For example, if your
project is built using setuptools, and the callable is a FooSender class in
the foobar.senders module, and you want it to be usable by setting method
= "foo", add the following to your setup.py:
setup(
...
entry_points={
"outgoing.senders": [
"foo = foobar.senders:FooSender",
],
},
...
)
Writing Password Schemes
A password scheme is implemented as a function that takes the value part of
a password = { scheme = value } entry as an argument and returns the
corresponding password as a str. If the function additionally accepts
arguments named host, username, and/or configpath (either
explicitly or via **kwargs), the corresponding values passed to
resolve_password() will be forwarded to the scheme function.
If the value structure is invalid, or if no password can be found, the
function should raise an InvalidPasswordError.
The last step of writing a password scheme is to package it in a Python project
and declare the function as an entry point in the outgoing.password_schemes
entry point group so that users can install & access it. For example, if your
project is built using setuptools, and the function is foo_scheme() in the
foobar.passwords module, and you want it to be usable by writing password
= { foo = some-value }, add the following to your setup.py:
setup(
...
entry_points={
"outgoing.password_schemes": [
"foo = foobar.passwords:foo_scheme",
],
},
...
)