Email builder
Email collects the parts of one styled mail, then sends it. It is a plain data holder: each method returns self, and it holds data, it does not grow behavior — no conditionals, no scheduling, no retries. Everything that can fail, fails in one place: .send().
from imio.emailkit import Email
Email("imio.pm.notifications:item_published").to(member).to(
"greffe@commune.be"
).cc(meeting_managers).reply_to("noreply@imio.be").with_context(
item=item, meeting=meeting
).attach(convocation_pdf, filename="convocation.pdf").send()
The template is not looked up in the constructor. Email("typo") is legal
and silent; TemplateNotFound arrives from .send(). That is deliberate, and
it matches how recipients and attachments behave — one place where things
fail, one place to look.
Recipients
- Name
.to(value)- Description
Add To recipients. Accepts, in any mix: an email string, a Plone member object, a userid, or an iterable of those.
- Name
.cc(value)- Description
Add Cc recipients. Same accepted values.
- Name
.bcc(value)- Description
Add Bcc recipients. The
Bccheader is removed before the message goes out, byMailHost, after it has collected the envelope recipients from it. Blind means blind.
- Name
.reply_to(value)- Description
Set the
Reply-Toaddresses. Same accepted values as.to()—.reply_to(item_author)should not need you to dig out an address by hand.
- Name
.sender(value)- Description
Override the
Fromaddress. Omitted,Fromis the site's configured sender.
How each value is resolved, and what happens when one cannot be: Recipients.
Content
- Name
.with_context(**context)- Description
Add names to the render context, exactly as
render()takes them.
- Name
.subject(value)- Description
Override the subject; an i18n msgid or a literal string. Omitted, the subject is the msgid in the template's registration, translated per language group.
- Name
.attach(source, filename=None, mimetype=None)- Description
Add an attachment. Callable repeatedly, order preserved. Nothing is read until
.send(). See Attachments.
Pass msgids through .with_context(), not translated strings.
.with_context() runs once, before .send() groups recipients by language.
A string you translated at that point would send one language's wording to
every recipient. Pass the msgid and let the render translate it per language
group.
Sending
- Name
.send(immediate=False)- Description
Resolve, render per language group, and hand each message to
MailHost. Returns theEmailMessageobjects it built, one per language group, in group order.
Three things happen, in this order:
- Resolution. Every recipient and every attachment is resolved before the first render, so a mistyped userid does not surface only after half the language groups have been queued. Raises
RecipientErrororAttachmentError, each reporting every problem it found rather than the first. - Per-language render. Recipients are grouped by resolved language; the template is rendered once per group, with the subject msgid translated accordingly. One message per group. FR and NL communes are handled with no caller effort.
- Queued delivery. Each message goes to
IMailHostinside the current transaction.
Transaction safety
Delivery is queued by default: if the transaction that built the mail aborts, nothing is sent. This is the behaviour you want in a content rule, an event subscriber or a form handler, and it is why there is no "did it send?" callback to write.
.send(immediate=True) bypasses the transaction and talks to the MTA now. It is the only escape hatch here; leave it alone unless you know why you want it.
Message assembly
email.message.EmailMessage, with set_content(text) followed by add_alternative(html, subtype="html"), correct headers and correct encoding. Nothing is hand-built by callers, ever. Attachments ride the same message and are identical across language groups.
Related
- Recipients — the
IEmailRecipientadapter and language resolution. - Attachments — every accepted source type.
- Errors — what
.send()raises and what each error carries. - Content-rule action — a thin caller of this builder, usable without code.
- Source:
imio/emailkit/email.py