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",
],
},
...
)