Package: mailers
Mailers for asyncioFeaturesfully typedfull utf-8 supportasync and sync sendingpluggable transportsmultiple built-in transports including: SMTP, file, null, in-memory, streaming, and console.message preprocessorsembeddablesattachments (with async and sync interfaces)message signing via Signer interface (DKIM bundled)message encryption via Encrypter interfacetrio support via anyiofallback transportsglobal From addresstemplated emailsUsagepipinstallmailers[aiosmtplib]Then create mailer:frommailersimportMailermailer=Mailer("smtp://user:password@localhost:25?timeout=2")awaitmailer.send_message(to="user@localhost",from_address="from@localhost",subject="Hello",text="World!")Compose messagesIf you need more control over the message, you can useEmailobject to construct email message and then send it
usingmailer.sendmethod.frommailersimportEmail,Mailermessage=Email(to="user@localhost",from_address="from@example.tld",cc="cc@example.com",bcc=["bcc@example.com"],text="Hello world!",html="<b>Hello world!</b>",)mailer=Mailer("smtp://")awaitmailer.send(message)Global From addressInstead of setting "From" header in every message, you can set it mailer-wide. Usefrom_addressargument of Mailer
class:mailer=Mailer(from_address="sender@localhost")The mailer will set From header with the given value to all messages that do not container From or Sender headers.Using Jinja templatesRequiresjinja2package installedYou can use Jinja to render templates. This way, yourtextandhtmlcan be rendered from a template.UseTemplatedMailerinstead of defaultMailerand set ajinja2.Environmentinstance.
Then, callsend_templated_message.importjinja2frommailersimportTemplatedMailerenv=jinja2.Environment(loader=jinja2.FileSystemLoader(["templates"]))mailer=TemplatedMailer("smtp://",env)mailer.send_templated_message(to="...",subject="Hello",text_template="mail.txt",html_template="mail.html",template_context={"user":"root"},)AttachmentsUseattach,attach_from_path,attach_from_path_syncmethods to attach files.frommailersimportEmailmessage=Email(to="user@localhost",from_address="from@example.tld",text="Hello world!")# attachments can be added on demandawaitmessage.attach_from_path("file.txt")# or use blocking sync versionmessage.attach_from_path_sync("file.txt")# attach from variablemessage.attach("CONTENTS","file.txt","text/plain")Embedding filesIn the same way as with attachments, you can inline file into your messages. This is commonly used to display embedded
images in the HTML body. Here are method you can useembed,embed_from_path,embed_from_path_sync.frommailersimportEmailmessage=Email(to="user@localhost",from_address="from@example.tld",html='Render me <img src="cid:img1">',)awaitmessage.embed_from_path(path="/path/to/image.png",name="img1")Note, that you have to add HTML part to embed files. Otherwise, they will be ignored.Message signaturesYou can sign messages (e.g. with DKIM) by passingsignerargument to theMailerinstance.signer=MySigner()mailer=Mailer(...,signer=signer)DKIM signingRequiresdkimpypackage installedYou may wish to add DKIM signature to your messages to prevent them from being put into the spam folder.Note, you need to installdkimpypackage before using this feature.frommailersimportMailerfrommailers.signers.dkimimportDKIMSignersigner=DKIMSigner(selector="default",private_key_path="/path/to/key.pem")# or you can put key content using private_key argumentsigner=DKIMSigner(selector="default",private_key="PRIVATE KEY GOES here...")mailer=Mailer("smtp://",signer=signer)Now all outgoing messages will be signed with DKIM method.The plugin signs "From", "To", "Subject" headers by default. Use "headers" argument to override it.Custom signersExtendmailers.Signerclass and implementsignmethod:fromemail.messageimportMessagefrommailersimportSignerclassMySigner(Signer):defsign(self,message:Message)->Message:# message signing code here...returnmessageEncryptersWhen encrypting a message, the entire message (including attachments) is encrypted using a certificate. Therefore, only
the recipients that have the corresponding private key can read the original message contents.encrypter=MyEncrypter()mailer=Mailer(...,encrypter=encrypter)Now all message content will be encrypted.Custom encryptersExtendmailers.Encrypterclass and implementencryptmethod:fromemail.messageimportMessagefrommailersimportEncrypterclassMyEncrypter(Encrypter):defencrypt(self,message:Message)->Message:# message encrypting code here...returnmessageHigh AvailabilityUseMultiTransportto provide a fallback transport. By default, the first transport is used but if it fails to send
the message, it will retry sending using next configured transport.frommailersimportMailer,MultiTransport,SMTPTransportprimary_transport=SMTPTransport()fallback_transport=SMTPTransport()mailer=Mailer(MultiTransport([primary_transport,fallback_transport]))PreprocessorsPreprocessors are function that mailer calls before sending. Preprocessors are simple functions that modify message
contents.Below you see an example preprocessor:fromemail.messageimportEmailMessagefrommailersimportMailerdefattach_html_preprocessor(message:EmailMessage)->EmailMessage:message.add_alternative(b"This is HTML body",subtype="html",charset="utf-8")returnmessagemailer=Mailer(preprocessors=[attach_html_preprocessor])CSS inlinerRequirestoronadopackage installedOut of the box we providemailers.preprocessors.css_inlinerutility that converts CSS classes into inline styles.TransportsSMTP transportRequiresaiosmtplibpackage installedSend messages via third-party SMTP servers.Class:mailers.transports.SMTPTransportdirectorysmtp://user:pass@hostname:port?timeout=&use_tls=1Options:host(string, default "localhost") - SMTP server hostport(string, default "25") - SMTP server portuser(string) - SMTP server loginpassword(string) - SMTP server login passworduse_tls(string, choices: "yes", "1", "on", "true") - use TLStimeout(int) - connection timeoutcert_file(string) - path to certificate filekey_file(string) - path to key fileFile transportWrite outgoing messages into a directory in EML format.Class:mailers.transports.FileTransportDSN:file:///tmp/mailsOptions:directory(string) path to a directoryNull transportDiscards outgoing messages. Takes no action on send.Class:mailers.transports.NullTransportDSN:null://Memory transportKeeps all outgoing messages in memory. Good for testing.Class:mailers.transports.InMemoryTransportDSN:memory://Options:storage(list of strings) - outgoing message containerYou can access the mailbox via ".mailbox" attribute.Example:frommailersimportMailer,InMemoryTransport,Emailtransport=InMemoryTransport([])mailer=Mailer(transport)awaitmailer.send(Email(...))assertlen(transport.mailbox)==1# here are all outgoing messagesStreaming transportWrites all messages into a writable stream. Ok for local development.Class:mailers.transports.StreamTransportDSN:unsupportedOptions:output(typing.IO) - a writable streamExample:importiofrommailersimportMailer,StreamTransporttransport=StreamTransport(output=io.StringIO())mailer=Mailer(transport)Console transportThis is a preconfigured subclass of streaming transport. Writes tosys.stderrby default.Class:mailers.transports.ConsoleTransportDSN:console://Options:output(typing.IO) - a writeable streamMulti transportThe purpose of this transport is to provide a developer an option to provide a fallback transport.
You can configure several channels andMultiTransportwill guarantee that at least one will deliver the message.Class:mailers.transports.MultiTransportDSN:-Options:transports(list[Transport]) - subtransportsCustom transports.Each transport must extendmailers.transports.Transportbase class.fromemail.messageimportMessagefrommailersimportMailer,TransportclassPrintTransport(Transport):asyncdefsend(self,message:Message)->None:print(str(message))mailer=Mailer(PrintTransport())