mail.message
import mail
Everything here is re-exported by
import mailis enough and the names are called asmail.*. Importingmail.messageon its own works too and reaches the same definitions.
A mail message: its headers, its body, and the tree of parts a body turns into once there is more than one thing in it.
Message is both what comes off the wire and what goes onto it. Parsing
one gives the whole MIME tree with every part decoded on demand;
building one from text, HTML and files gives back a message any mail
server will accept.
import mail
var note = mail.message()
.set_from('Richard Ore <richard@example.com>')
.add_to('ann@example.com')
.set_subject('Quarterly report')
.set_text('The numbers are attached.')
echo note.subject()
echo note.content_type().mime_type()
Quarterly report
text/plain
A message with both text and HTML in it becomes a
multipart/alternative; add a file and the whole thing is wrapped in a
multipart/mixed. None of that has to be spelled out: the shape follows
from what was put in.
Functions
text_part()
mail.text_part(text: string, charset: ?string, subtype: ?string) -> Message
Builds one text part.
Parameters
text(string)charset(?string) —utf-8, orus-asciiwhen the text needs nothing more. No other set can be written.subtype(?string) —plainwhen not given.
Returns Message
Raises MessageError if charset names a set this cannot write.
attachment()
mail.attachment(data) -> Attachment
Starts an attachment from what is in it.
import mail
echo mail.attachment('some bytes').set_filename('notes.txt').to_string()
<Attachment notes.txt, 10 bytes>
Parameters
data(string|bytes)
Returns Attachment
multipart()
mail.multipart(subtype: string, parts: list)
new_message_id()
mail.new_message_id(domain: ?string) -> string
An identifier for a message, unique enough that no two ever collide.
Parameters
domain(?string) — The right hand side. The machine’s own name when not given.
Returns string — including the angle brackets a header needs.
parse()
mail.parse(data) -> Message
Reads a message.
Parameters
data(string|bytes)
Returns Message
message()
mail.message() -> Message
Starts a message.
What goes in it is said one call at a time, and every call hands the message back so they read as one:
import mail
var note = mail.message()
.set_from('Richard Ore <richard@example.com>')
.add_to('ann@example.com')
.set_subject('Quarterly report')
.set_text('The numbers are in.')
echo note.subject()
echo note.content_type().mime_type()
Quarterly report
text/plain
The shape follows from what is said. Text alone is a text/plain
message; adding HTML makes it a multipart/alternative; attaching a
file wraps the whole thing in a multipart/mixed. None of that has to
be arranged by hand.
A message starts dated, since the time it was written is the time it was written. It gets its identifier when it is sent, because that is when the domain it belongs to is settled.
Returns Message
Classes
Message
class mail.Message
One message, or one part of one.
A part is a message too: it has headers and a body, and its body may be more parts. The same class covers both, so walking a message and reading a standalone one are the same code.
- printable — has a
@to_string(), soechoandprint()show something useful
Constructor
mail.Message(headers, source)
Builds a message.
Most callers want mail.message(), which fills one in, or
mail.parse(), which reads one off the wire.
Parameters
headers(?Headers) — The headers to start with.source(?any) — Bytes or text to read the message from, in which caseheadersis ignored.
Message.parse()
mail.Message.parse(data) -> Message
Reads a message.
Nothing is decoded up front. The tree is built, and each part decodes its own body the first time something asks for it, so reading the subject of a message with a large attachment in it costs nothing more than reading the headers.
import mail
var note = mail.parse(
'From: a@example.com\r\n'
+ 'Subject: Hi\r\n'
+ '\r\n'
+ 'Hello there.\r\n'
)
echo note.subject()
echo note.text()
Hi
Hello there.
Parameters
data(string|bytes)
Returns Message
Raises MessageError if the headers cannot be read.
Message.content_type()
mail.Message.content_type() -> ContentType
The Content-Type of this part.
Returns ContentType
Message.is_multipart()
mail.Message.is_multipart() -> bool
Whether this part holds other parts.
Returns bool
Message.parts()
mail.Message.parts() -> list
The parts directly inside this one, which is empty for a part that holds content rather than other parts.
Returns list — of Message
Message.walk()
mail.Message.walk() -> list
Every part of the message including this one, outermost first.
for part in note.walk() {
echo part.content_type().mime_type()
}
Returns list — of Message
Message.body_bytes()
mail.Message.body_bytes() -> bytes
This part’s body, decoded from whatever transfer encoding it arrived in.
A part that holds other parts has no body of its own and gives back nothing.
Returns bytes
Message.text()
mail.Message.text() -> string
This part’s body as text, decoded from its transfer encoding and then from its character set.
Returns string
Message.raw_body()
mail.Message.raw_body() -> bytes
The body still encoded, exactly as it sits in the message.
Returns bytes
Message.set_raw_body()
mail.Message.set_raw_body(data) -> Message
Replaces the body with bytes that are already in the transfer encoding the headers name, and drops any parts this held.
The encoding is not applied here and not checked. set_text() and
attach() are the calls that do both.
Parameters
data(string|bytes)
Returns Message — itself.
Message.set_parts()
mail.Message.set_parts(parts: list) -> Message
Replaces the parts this one holds.
Parameters
parts(list) —Messagevalues.
Returns Message — itself.
Message.is_empty()
mail.Message.is_empty() -> bool
Whether this part holds nothing at all: no body and no parts.
Returns bool
Message.source_headers()
mail.Message.source_headers() -> string|nil
The bytes this part’s headers were read from, or nil for a part that
was built rather than parsed.
A signature covers the message as it was written, not as it would be written again, which is what this is for.
Returns string|nil
Message.source_body()
mail.Message.source_body() -> bytes|nil
The bytes this part’s body was read from, or nil for a part that was
built rather than parsed.
Returns bytes|nil
Message.subject()
mail.Message.subject() -> string
The subject, with any encoded words in it decoded.
Returns string — empty when the message has no subject.
Message.set_subject()
mail.Message.set_subject(text: string) -> Message
Sets the subject, encoding it if it holds characters a header cannot carry.
Parameters
text(string)
Returns Message — itself.
Message.sender()
mail.Message.sender() -> Address|nil
The sender, or nil when the message has no From.
Returns Address|nil
Message.to()
mail.Message.to() -> list
The addresses in To.
Returns list — of Address
Message.cc()
mail.Message.cc() -> list
The addresses in Cc.
Returns list — of Address
Message.bcc()
mail.Message.bcc() -> list
The addresses in Bcc.
A message that has been sent should not have this header at all, since
the whole point of a blind copy is that the other recipients cannot see
it. mail.send() strips it before handing the message over, after
taking the recipients from it.
Returns list — of Address
Message.reply_to()
mail.Message.reply_to() -> list
The addresses in Reply-To, falling back to the sender when there is no
such header, which is what a mail client does.
Returns list — of Address
Message.recipients()
mail.Message.recipients() -> list
Everyone the message is addressed to: To, Cc and Bcc together,
with duplicates removed.
Returns list — of Address
Message.sent_at()
mail.Message.sent_at() -> Date|nil
The Date header as a date, or nil when it is absent or unreadable.
Returns Date|nil
Message.message_id()
mail.Message.message_id() -> string|nil
The Message-ID, with the angle brackets left on, or nil.
Returns string|nil
Message.references()
mail.Message.references() -> list
The message identifiers in References, oldest first, which is the
chain of replies this message belongs to.
Returns list — of string
Message.filename()
mail.Message.filename() -> string|nil
The name this part suggests being saved under, from its disposition or,
failing that, its type. nil when it suggests none.
Returns string|nil
Message.is_attachment()
mail.Message.is_attachment() -> bool
Whether this part is meant to be offered as a file rather than shown in place.
A part is an attachment when it says so, and also when it says nothing but carries a filename, which is what older mailers do.
Returns bool
Message.find_part()
mail.Message.find_part(mime_type: string) -> Message|nil
The first part of a given type anywhere in the message, or nil.
Parameters
mime_type(string) — Astext/html.
Returns Message|nil
Message.text_body()
mail.Message.text_body() -> string|nil
The plain text of the message, wherever in the tree it is, or nil when
there is none.
Returns string|nil
Message.html_body()
mail.Message.html_body() -> string|nil
The HTML of the message, wherever in the tree it is, or nil when there
is none.
Returns string|nil
Message.attachments()
mail.Message.attachments() -> list
Every part of the message that is an attachment.
Returns list — of Message
Message.take_content()
mail.Message.take_content() -> Message
Moves this part’s content into a part of its own and hands it back, leaving this one holding the message-level headers and nothing else.
This is what turns a message into the first child of the multipart that
is about to replace it, which is why attach() can wrap a message
without the caller rebuilding it.
Returns Message
Message.become_multipart()
mail.Message.become_multipart(subtype: string, parts: list) -> Message
Turns this part into a multipart holding the given parts, under a boundary nothing inside it can contain.
Parameters
subtype(string) —mixed,alternative,relatedordigest.parts(list) — TheMessagevalues to put in it.
Returns Message — itself.
Message.set_from()
mail.Message.set_from(person) -> Message
Sets who the message is from, replacing whoever was there.
note.set_from('Richard Ore <richard@example.com>')
Parameters
person(string|Address)
Returns Message — itself.
Message.set_sender()
mail.Message.set_sender(person) -> Message
Sets the Sender header, which says who actually put the message on the
wire when that is not who it is from.
Parameters
person(string|Address)
Returns Message — itself.
Message.add_to()
mail.Message.add_to(people) -> Message
Adds recipients, keeping any already there.
note.add_to('ann@example.com').add_to('bob@example.com')
Parameters
people(string|Address|list)
Returns Message — itself.
Message.add_cc()
mail.Message.add_cc(people) -> Message
Adds copied recipients, keeping any already there.
Parameters
people(string|Address|list)
Returns Message — itself.
Message.add_bcc()
mail.Message.add_bcc(people) -> Message
Adds blind copied recipients.
The header is removed before the message is handed to a server, after the recipients have been taken from it, which is the whole point of a blind copy.
Parameters
people(string|Address|list)
Returns Message — itself.
Message.set_to()
mail.Message.set_to(people) -> Message
Sets the recipients, replacing any already there.
Parameters
people(string|Address|list)
Returns Message — itself.
Message.set_cc()
mail.Message.set_cc(people) -> Message
Sets the copied recipients, replacing any already there.
Parameters
people(string|Address|list)
Returns Message — itself.
Message.set_bcc()
mail.Message.set_bcc(people) -> Message
Sets the blind copied recipients, replacing any already there.
Parameters
people(string|Address|list)
Returns Message — itself.
Message.set_reply_to()
mail.Message.set_reply_to(people) -> Message
Sets where replies should go, when that is not the sender.
Parameters
people(string|Address|list)
Returns Message — itself.
Message.set_header()
mail.Message.set_header(name: string, value) -> Message
Sets any header at all, replacing one already there.
The value is written exactly as given, so a header that needs encoding
needs it applied first. set_subject() and the address setters do that
for the headers where it matters.
Parameters
name(string)value(any)
Returns Message — itself.
Message.add_header()
mail.Message.add_header(name: string, value) -> Message
Adds a header, keeping any already there. Received is the header this
is for.
Parameters
name(string)value(any)
Returns Message — itself.
Message.set_date()
mail.Message.set_date(moment) -> Message
Sets when the message was written.
A message built by mail.message() is dated for you, so this is for
saying otherwise.
Parameters
moment(?Date) — The current time when not given.
Returns Message — itself.
Message.set_message_id()
mail.Message.set_message_id(id: ?string) -> Message
Sets the message’s own identifier.
Parameters
id(?string) — A fresh one, built from the sender’s domain, when not given.
Returns Message — itself.
Message.set_in_reply_to()
mail.Message.set_in_reply_to(id: string) -> Message
Marks the message as a reply to another, which is what puts the two in the same conversation in a mail client.
reply.set_in_reply_to(original.message_id())
.set_references(original.references() + [original.message_id()])
Parameters
id(string) — The identifier of the message being answered.
Returns Message — itself.
Message.set_references()
mail.Message.set_references(ids) -> Message
Sets the chain of identifiers this message belongs to, oldest first.
Parameters
ids(list|string)
Returns Message — itself.
Message.set_text()
mail.Message.set_text(text: string, charset: ?string) -> Message
Sets the plain text of the message.
Replaces the text that is already there, wherever in the tree it is. A
message that has HTML but no text becomes a multipart/alternative
holding both.
Parameters
text(string)charset(?string) —utf-8when not given.
Returns Message — itself.
Message.set_html()
mail.Message.set_html(html: string, charset: ?string) -> Message
Sets the HTML of the message.
Parameters
html(string)charset(?string) —utf-8when not given.
Returns Message — itself.
Message.attach()
mail.Message.attach(part) -> Message
Adds an attachment to the message.
The message becomes a multipart/mixed if it is not one already, with
whatever it held before as the first part. That happens once, however
many attachments are added.
note.attach(mail.attachment(figures).set_filename('figures.csv'))
note.attach(mail.Attachment.from_file('reports/q3.pdf'))
Parameters
part(Attachment|Message) — An attachment, or a part that is already built.
Returns Message — itself.
Raises MessageError if handed anything else.
Message.embed()
mail.Message.embed(part) -> string
Adds something to be shown inside the HTML rather than offered
separately, and returns the reference to point an <img> at.
The message becomes a multipart/related around the content it already
held, which is what tells a reader the two belong together.
var source = note.embed(mail.attachment(logo).set_filename('logo.png'))
note.set_html('<img src="${source}">')
Parameters
part(Attachment|Message)
Returns string — the cid: URL that refers to it.
Raises MessageError if handed anything else.
Message.to_bytes()
mail.Message.to_bytes() -> bytes
The whole message, ready to be written to a socket or a file.
Lines end with a carriage return and a newline, which is what the protocols require whatever the local convention is.
Returns bytes
Message.to_string()
mail.Message.to_string() -> string
The whole message as text.
Returns string
Attachment
class mail.Attachment
Something a message carries alongside what it says: a file to offer, or an image the HTML shows.
Built one call at a time, the way a message is, and handed to
Message.attach() or Message.embed().
import mail
var figures = mail.attachment('name,total\nengines,41')
.set_filename('figures.csv')
echo figures.filename()
echo figures.content_type()
echo figures.size()
figures.csv
text/csv
24
The media type follows from the filename unless it is set. The transfer encoding follows from the contents, and a file is always encoded rather than sent as it is, because bytes that happen to look like text are still bytes.
- printable — has a
@to_string(), soechoandprint()show something useful
Constructor
mail.Attachment(data)
Parameters
data(string|bytes) — The contents.
Attachment.from_file()
mail.Attachment.from_file(path: string) -> Attachment
Reads the contents from a file on disk, taking the name from the path.
note.attach(Attachment.from_file('reports/q3.pdf'))
Parameters
path(string)
Returns Attachment
Raises Error if the file cannot be read.
Attachment.set_filename()
mail.Attachment.set_filename(name: string) -> Attachment
Sets the name to offer the file under.
A name with characters a header cannot carry is encoded, and a long one is split across continuations, on the way out.
Parameters
name(string)
Returns Attachment — itself.
Attachment.set_content_type()
mail.Attachment.set_content_type(type: string) -> Attachment
Sets the media type, for when the filename does not settle it.
Parameters
type(string) — Asapplication/pdf.
Returns Attachment — itself.
Attachment.set_disposition()
mail.Attachment.set_disposition(kind: string) -> Attachment
Sets whether the part is offered as a file or shown where it sits.
embed() sets this to inline itself, so it is only worth setting for
a part that is neither quite.
Parameters
kind(string) —attachmentorinline.
Returns Attachment — itself.
Attachment.set_cid()
mail.Attachment.set_cid(id: string) -> Attachment
Sets the identifier HTML refers to this part by.
embed() invents one when there is none, so this is for a reference
that has to be predictable.
Parameters
id(string) — With or without the angle brackets.
Returns Attachment — itself.
Attachment.set_encoding()
mail.Attachment.set_encoding(name: string) -> Attachment
Sets the transfer encoding, overriding the one the contents would have chosen.
Parameters
name(string) — One ofmail.encoding.TRANSFER_ENCODINGS.
Returns Attachment — itself.
Attachment.set_description()
mail.Attachment.set_description(text: string) -> Attachment
Sets a human-readable description, which some mail clients show beside the file.
Parameters
text(string)
Returns Attachment — itself.
Attachment.filename()
mail.Attachment.filename() -> string|nil
The name this will be offered under, or nil.
Returns string|nil
Attachment.content_type()
mail.Attachment.content_type() -> string
The media type this will be sent as, worked out from the filename when it was not set.
Returns string
Attachment.disposition()
mail.Attachment.disposition() -> string
Whether the part is offered as a file or shown where it sits.
Returns string
Attachment.cid()
mail.Attachment.cid() -> string|nil
The identifier HTML refers to this part by, or nil.
Returns string|nil
Attachment.size()
mail.Attachment.size() -> number
How many bytes the contents are.
Returns number
Attachment.data()
mail.Attachment.data() -> bytes
The contents.
Returns bytes
Attachment.to_part()
mail.Attachment.to_part() -> Message
Turns this into the message part that goes on the wire.
attach() and embed() call it. It is here for a program building a
tree by hand.
Returns Message
Attachment.to_string()
mail.Attachment.to_string()
2026, Richard Ore and Zuri contributors