Package: mailbits
GitHub|PyPI|Issues|Changelogmailbitsprovides a small assortment of functions for working with the
Python standard library’sMessage/EmailMessage,Address, andGrouptypes, as well as a couple other features.  It can parse & reassemble
Content-Type strings, convert instances of the oldMessageclass to the newEmailMessage, convertMessage&EmailMessageinstances into
structureddicts, parse addresses, format address lists, and extract
recipients’ raw e-mail addresses from anEmailMessage.Installationmailbitsrequires Python 3.6 or higher.  Just usepipfor Python 3 (You have pip, right?) to install it:python3 -m pip install mailbitsAPIContentTypeTheContentTypeclass provides a representation of a parsed Content-Type
header value.  Parse Content-Type strings with theparse()classmethod,
inspect the parts via thecontent_type,maintype,subtype, andparamsattributes (the last three of which can be mutated), convert back to
a string withstr(), and convert to ASCII bytes using encoded words for
non-ASCII withbytes().>>> from mailbits import ContentType
>>> ct = ContentType.parse("text/plain; charset=utf-8; name*=utf-8''r%C3%A9sum%C3%A9.txt")
>>> ct
ContentType(maintype='text', subtype='plain', params={'charset': 'utf-8', 'name': 'résumé.txt'})
>>> ct.content_type
'text/plain'
>>> str(ct)
'text/plain; charset="utf-8"; name="résumé.txt"'
>>> bytes(ct)
b'text/plain; charset="utf-8"; name*=utf-8\'\'r%C3%A9sum%C3%A9.txt'email2dict()classMessageDict(TypedDict):unixfrom:Optional[str]headers:Dict[str,Any]preamble:Optional[str]content:Anyepilogue:Optional[str]mailbits.email2dict(msg:email.message.Message,include_all:bool=False)->MessageDictConvert aMessageobject to adict.  All encoded text & bytes are
decoded into their natural values.Need to examine aMessagebut find the builtin Python API too fiddly?  Need
to check that aMessagehas the content & structure you expect?  Need to
compare twoMessageinstances for equality?  Need to pretty-print the
structure of aMessage?  Thenemail2dict()has your back.By default, any information specific to how the message is encoded (Content-Type
parameters, Content-Transfer-Encoding, etc.) is not reported, as the focus is
on the actual content rather than the choices made in representing it.  To
include this information anyway, setinclude_alltoTrue.The output structure has the following fields:unixfromThe “From “ line marking the start of the message in a mbox, if anyheadersAdictmapping lowercased header field names to values.  The following
headers have special representations:subjectA single stringfrom,to,cc,bcc,resent-from,resent-to,resent-cc,resent-bcc,reply-toA list of groups and/or addresses.  Addresses are represented asdicts with two string fields:display_name(an empty string if
not given) andaddress.  Groups are represented asdicts with
agroupfield giving the name of the group and anaddressesfield giving a list of addresses in the group.message-idA single stringcontent-typeAdictcontaining acontent_typefield (a string of the formmaintype/subtype, e.g.,"text/plain") and aparamsfield (adictof string keys & values).  Thecharsetandboundaryparameters are discarded unlessinclude_allisTrue.dateAdatetime.datetimeinstanceorig-dateAdatetime.datetimeinstanceresent-dateA list ofdatetime.datetimeinstancessenderA single addressdictresent-senderA list of addressdictscontent-dispositionAdictcontaining adispositionfield (value either"inline"or"attachment") and aparamsfield (adictof
string keys & values)content-transfer-encodingA single string.  This header is discarded unlessinclude_allisTrue.mime-versionA single string.  This header is discarded unlessinclude_allisTrue.All other headers are represented as lists of strings.preambleThe message’spreamblecontentIf the message is multipart, this is a list of messagedicts,
structured the same way as the top-leveldict.  If the message’s
Content-Type ismessage/rfc822ormessage/external-body, this is a
single messagedict.  If the message’s Content-Type istext/*, this
is astrgiving the contents of the message.  Otherwise, it is abytesgiving the contents of the message.epilogueThe message’sepilogueAn example: Theemailexamples pagein the Python docs includes an
example of constructing an HTML e-mail with an alternative plain text version
(It’s the one with the subject “Ayons asperges pour le déjeuner”).  Passing the
resultingEmailMessageobject toemail2dict()produces the following
output structure:{"unixfrom":None,"headers":{"subject":"Ayons asperges pour le déjeuner","from":[{"display_name":"Pepé Le Pew","address":"pepe@example.com",},],"to":[{"display_name":"Penelope Pussycat","address":"penelope@example.com",},{"display_name":"Fabrette Pussycat","address":"fabrette@example.com",},],"content-type":{"content_type":"multipart/alternative","params":{},},},"preamble":None,"content":[{"unixfrom":None,"headers":{"content-type":{"content_type":"text/plain","params":{},},},"preamble":None,"content":("Salut!\n""\n""Cela ressemble à un excellent recipie[1] déjeuner.\n""\n""[1] http://www.yummly.com/recipe/Roasted-Asparagus-Epicurious-203718\n""\n""--Pepé\n"),"epilogue":None,},{"unixfrom":None,"headers":{"content-type":{"content_type":"multipart/related","params":{},},},"preamble":None,"content":[{"unixfrom":None,"headers":{"content-type":{"content_type":"text/html","params":{},},},"preamble":None,"content":("<html>\n""  <head></head>\n""  <body>\n""    <p>Salut!</p>\n""    <p>Cela ressemble à un excellent\n""        <a href=\"http://www.yummly.com/recipe/Roasted-Asparagus-""Epicurious-203718\">\n""            recipie\n""        </a> déjeuner.\n""    </p>\n""    <img src=\"cid:RANDOM_MESSAGE_ID\"/>\n""  </body>\n""</html>\n"),"epilogue":None,},{"unixfrom":None,"headers":{"content-type":{"content_type":"image/png","params":{},},"content-disposition":{"disposition":"inline","params":{},},"content-id":["<RANDOM_MESSAGE_ID>"],},"preamble":None,"content":b'IMAGE BLOB',"epilogue":None,},],"epilogue":None,},],"epilogue":None,}format_addresses()mailbits.format_addresses(addresses:Iterable[Union[str,Address,Group]],encode:bool=False)->strConvert an iterable of e-mail address strings (of the form
“foo@example.com”, without angle brackets or a display name),email.headerregistry.Addressobjects, and/oremail.headerregistry.Groupobjects into a formatted string.  IfencodeisFalse(the default),
non-ASCII characters are left as-is.  If it isTrue, non-ASCII display
names are converted intoRFC 2047encoded words, and non-ASCII domain names
are encoded using Punycode.message2email()mailbits.message2email(msg:email.message.Message)->email.message.EmailMessageConvert an instance of the oldMessageclass (or one of its subclasses,
like amailboxmessage class) to an instance of the newEmailMessageclass with thedefaultpolicy.  Ifmsgis already anEmailMessage,
it is returned unchanged.parse_address()mailbits.parse_address(s:str)->email.headerregistry.AddressParse a single e-mail address — either a raw address like “foo@example.com”
or a combined display name & address like “Fabian Oh <foo@example.com>”
into anAddressobject.parse_addresses()mailbits.parse_addresses(s:Union[str,email.headerregistry.AddressHeader])\->List[Union[email.headerregistry.Address,email.headerregistry.Group]]Parse a formatted list of e-mail addresses or the contents of anEmailMessage’s “To”, “CC”, “BCC”, etc. header into a list ofAddressand/orGroupobjects.recipient_addresses()mailbits.recipient_addresses(msg:email.message.EmailMessage)->List[str]Return a sorted list of all of the distinct e-mail addresses (not including
display names) in anEmailMessage’s combined “To”, “CC”, and “BCC” headers.