Flanker - email address and MIME parsing for Python
.. image:: https://travis-ci.org/mailgun/flanker.svg?branch=master
:target: https://travis-ci.org/mailgun/flanker
.. image:: https://coveralls.io/repos/github/mailgun/flanker/badge.svg?branch=master
:target: https://coveralls.io/github/mailgun/flanker?branch=master
Flanker is an open source parsing library written in Python by the Mailgun Team.
Flanker currently consists of an address parsing library (flanker.addresslib
) as
well as a MIME parsing library (flanker.mime
).
Detailed documentation is provided in the User Manual <https://github.com/mailgun/flanker/blob/master/docs/User%20Manual.md>
_ as well as the
API Reference <https://github.com/mailgun/flanker/blob/master/docs/API%20Reference.md>
_. A Quickstart Guide is provided below.
Python Versions
Flanker is heavily used by Mailgun <www.mailgun.com>
_ in production with
Python 2.7. The current production version is v0.8.5.
Support for Python 3 was added in v0.9.0 by popular demand from the community.
We are not using Flanker with Python 3 in the house. All we know is that tests
pass with Python 3.6, so use at your own risk. Feel free to report Python 3
specific issues if you see any.
Installing
You can install flanker via pip
or clone the repo from GitHub.
You'll need Python headers files before you start working with flanker, so install them first:
.. code-block:: bash
ubuntu
sudo apt-get install python-dev
fedora
sudo yum install python-devel
If you are using pip
, simply type:
.. code-block:: bash
pip install flanker
If you are cloning from GitHub, you can type:
.. code-block:: bash
git clone git@github.com:mailgun/flanker.git
cd flanker
pip install -e .
Address Parsing
To parse a single mailbox (display name as well as email address):
.. code-block:: py
from flanker.addresslib import address
address.parse('Foo foo@example.com')
Foo foo@example.com
An invalid address is returned as None
:
.. code-block:: py
from flanker.addresslib import address
print address.parse('@example.com')
None
To parse a single email address (no display name):
.. code-block:: py
from flanker.addresslib import address
address.parse('foo@example.com', addr_spec_only=True)
foo@example.com
To parse an address list:
.. code-block:: py
from flanker.addresslib import address
address.parse_list(['foo@example.com, bar@example.com, @example.com'])
[foo@example.com, bar@example.com]
To parse an address list as well as return a tuple containing the parsed
addresses and the unparsable portions
.. code-block:: py
from flanker.addresslib import address
address.parse_list(['foo@example.com, bar@example.com, @example.com'], as_tuple=True)
[foo@example.com, bar@example.com], ['@example.com']
To parse an address list in strict mode:
.. code-block:: py
from flanker.addresslib import address
address.parse_list(['foo@example.com, bar@example.com, @example.com'], strict=True)
[foo@example.com, bar@example.com]
To validate an email address (parse as well as DNS, MX existence, and ESP grammar checks):
.. code-block:: py
from flanker.addresslib import address
address.validate_address('foo@mailgun.com')
foo@mailgun.com
To validate an address list:
.. code-block:: py
from flanker.addresslib import address
address.validate_list(['foo@mailgun.com, bar@mailgun.com, @mailgun.com'], as_tuple=True)
([foo@mailgun.com, bar@mailgun.com], ['@mailgun.com'])
MIME Parsing
For the following examples, message_string
will be set to the following MIME message:
::
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary=001a11c1d71697c7f004e6856996
From: Bob bob@example.com
To: Alice alice@example.com
Subject: hello, world
Date: Mon, 16 Sep 2013 12:43:03 -0700
--001a11c1d71697c7f004e6856996
Content-Type: text/plain; charset=us-ascii
Hello, Alice
--001a11c1d71697c7f004e6856996
Content-Type: text/html; charset=us-ascii
Hello, Alice
--001a11c1d71697c7f004e6856996--
To parse a MIME message:
.. code-block:: py
from flanker import mime
msg = mime.from_string(message_string)
MIME message headers (unicode multi-value dictionary with headers):
.. code-block:: py
from flanker import mime
msg = mime.from_string(message_string)
msg.headers.items()
[('Mime-Version', '1.0'),
('Content-Type',
('multipart/alternative', {'boundary': u'001a11c1d71697c7f004e6856996'})),
('From', 'Bob bob@example.com'),
('To', 'Alice alice@example.com'),
('Subject', 'hello, world'),
('Date', 'Mon, 16 Sep 2013 12:43:03 -0700')]
Useful content_type member with predicates:
.. code-block:: py
from flanker import mime
msg = mime.from_string(message_string)
msg.content_type.is_multipart()
True
msg.content_type.is_singlepart()
False
msg.content_type.is_message_container()
False
Decoded body of a message:
.. code-block:: py
from flanker import mime
msg = mime.from_string(message_string)
None because message is multipart
print msg.body
None
for part in msg.parts:
print 'Content-Type: {} Body: {}'.format(part, part.body)
Content-Type: (text/plain) Body: Hello, Alice
Content-Type: (text/html) Body:
Hello, Alice
None because no enclosed messages exist
print msg.enclosed
None