Package: premailer
Looking for sponsorsThis project is actively looking for corporate sponsorship. If you want
to help making this an active project considerpinging
Peterand we can talk about putting
up logos and links to your company.Python versionsOurtox.inimakes sure premailer works in:Python 3.4Python 3.5Python 3.6Python 3.7Python 3.8PyPyTurns CSS blocks into style attributesWhen you send HTML emails you can’t use style tags but instead you have
to put inlinestyleattributes on every element. So from this:<html><styletype="text/css">h1{border:1pxsolidblack}p{color:red;}</style><h1style="font-weight:bolder">Peter</h1><p>Hej</p></html>You want this:<html><h1style="font-weight:bolder; border:1px solid black">Peter</h1><pstyle="color:red">Hej</p></html>premailer does this. It parses an HTML page, looks upstyleblocks
and parses the CSS. It then uses thelxml.htmlparser to modify the
DOM tree of the page accordingly.Warning!
By default, premailer will attempt to download any external stylesheets by URL over the Internet.
If you want to prevent this you can use theallow_network=Falseoption.Getting startedIf you haven’t already done so, installpremailerfirst:$ pip install premailerNext, the most basic use is to use the shortcut function, like this:>>>frompremailerimporttransform>>>print(transform("""
...         <html>
...         <style type="text/css">
...         h1 { border:1px solid black }
...         p { color:red;}
...         p::first-letter { float:left; }
...         </style>
...         <style type="text/css" data-premailer="ignore">
...         h1 { color:blue; }
...         </style>
...         <h1 style="font-weight:bolder">Peter</h1>
...         <p>Hej</p>
...         </html>
... """))<html><head><styletype="text/css">p::first-letter{float:left}</style><styletype="text/css">h1{color:blue;}</style></head><body><h1style="border:1px solid black; font-weight:bolder">Peter</h1><pstyle="color:red">Hej</p></body></html>Thetransformshortcut function transforms the given HTML using the defaults for all options:base_url=None,# Optional URL prepended to all relative links (both stylesheets and internal)disable_link_rewrites=False,# Allow link rewrites (e.g. using base_url)preserve_internal_links=False,# Do not preserve links to named anchors when using base_urlpreserve_inline_attachments=True,# Preserve links with cid: scheme when base_url is specifiedpreserve_handlebar_syntax=False# Preserve handlebar syntax from being encodedexclude_pseudoclasses=True,# Ignore pseudoclasses when processing styleskeep_style_tags=False,# Discard original style taginclude_star_selectors=False,# Ignore star selectors when processing stylesremove_classes=False,# Leave class attributes on HTML elementscapitalize_float_margin=False,# Do not capitalize float and margin propertiesstrip_important=True,# Remove !important from property valuesexternal_styles=None,# Optional list of URLs to load and parsecss_text=None,# Optional CSS text to parsemethod="html",# Parse input as HTML (as opposed to "xml")base_path=None,# Optional base path to stylesheet in your file systemdisable_basic_attributes=None,# Optional list of attribute names to preserve on HTML elementsdisable_validation=False,# Validate CSS when parsing it with cssutilscache_css_parsing=True,# Do cache parsed output for CSScssutils_logging_handler=None,# See "Capturing logging from cssutils" belowcssutils_logging_level=None,disable_leftover_css=False,# Output CSS that was not inlined into the HEADalign_floating_images=True,# Add align attribute for floated imagesremove_unset_properties=True# Remove CSS properties if their value is unset when mergedallow_network=True# allow network access to fetch linked css filesallow_insecure_ssl=False# Don't allow unverified SSL certificates for external linksallow_loading_external_files=False# Allow loading any non-HTTP external file URLsession=None# Session used for http requests - supply your own for caching or to provide authenticationFor more advanced options, check out the code of thePremailerclass
and all its options in its constructor.You can also use premailer from the command line by using its main
module.$ python -m premailer -h
usage: python -m premailer [options]

optional arguments:
-h, --help            show this help message and exit
-f [INFILE], --file [INFILE]
                      Specifies the input file. The default is stdin.
-o [OUTFILE], --output [OUTFILE]
                      Specifies the output file. The default is stdout.
--base-url BASE_URL
--remove-internal-links PRESERVE_INTERNAL_LINKS
                      Remove links that start with a '#' like anchors.
--exclude-pseudoclasses
                      Pseudo classes like p:last-child', p:first-child, etc
--preserve-style-tags
                      Do not delete <style></style> tags from the html
                      document.
--remove-star-selectors
                      All wildcard selectors like '* {color: black}' will be
                      removed.
--remove-classes      Remove all class attributes from all elements
--strip-important     Remove '!important' for all css declarations.
--method METHOD       The type of html to output. 'html' for HTML, 'xml' for
                      XHTML.
--base-path BASE_PATH
                      The base path for all external stylsheets.
--external-style EXTERNAL_STYLES
                      The path to an external stylesheet to be loaded.
--disable-basic-attributes DISABLE_BASIC_ATTRIBUTES
                      Disable provided basic attributes (comma separated)
--disable-validation  Disable CSSParser validation of attributes and values
--pretty              Pretty-print the outputted HTML.
--allow-insecure-ssl  Skip SSL certificate verification for external URLs.
--allow-loading-external-files Allow opening any non-HTTP external file URL.A basic example:$ python -m premailer --base-url=http://google.com/ -f newsletter.html
<html>
<head><style>.heading { color:red; }</style></head>
<body><h1 class="heading" style="color:red"><a href="http://google.com/">Title</a></h1></body>
</html>The command line interface supports standard input.$ echo '<style>.heading { color:red; }</style><h1 class="heading"><a href="/">Title</a></h1>' | python -m premailer --base-url=http://google.com/
<html>
<head><style>.heading { color:red; }</style></head>
<body><h1 class="heading" style="color:red"><a href="http://google.com/">Title</a></h1></body>
</html>Turning relative URLs into absolute URLsAnother thing premailer can do for you is to turn relative URLs (e.g.
“/some/page.html” into “http://www.peterbe.com/some/page.html”). It does
this to allhrefandsrcattributes that don’t have a://part in it. For example, turning this:<html><body><ahref="/">Home</a><ahref="page.html">Page</a><ahref="http://crosstips.org">External</a><imgsrc="/folder/">Folder</a></body></html>Into this:<html><body><ahref="http://www.peterbe.com/">Home</a><ahref="http://www.peterbe.com/page.html">Page</a><ahref="http://crosstips.org">External</a><imgsrc="http://www.peterbe.com/folder/">Folder</a></body></html>by usingtransform('...',base_url='http://www.peterbe.com/').Ignore certain<style>or<link>tagsSuppose you have a style tag that you don’t want to have processed and
transformed you can simply set a data attribute on the tag like:<head><style>/* this gets processed */</style><styledata-premailer="ignore">/* this gets ignored */</style></head>That tag gets completely ignored except when the HTML is processed, the
attributedata-premaileris removed.It works equally for a<link>tag like:<head><linkrel="stylesheet"href="foo.css"data-premailer="ignore"></head>HTML attributes created additionallyCertain HTML attributes are also created on the HTML if the CSS contains
any ones that are easily translated into HTML attributes. For example,
if you have this CSS:td {background-color:#eee;}then this is
transformed intostyle="background-color:#eee"and as an HTML
attributebgcolor="#eee".Having these extra attributes basically as a “back up” for really shit
email clients that can’t even take the style attributes. A lot of
professional HTML newsletters such as Amazon’s use this. You can disable
some attributes indisable_basic_attributes.Capturing logging fromcssutilscssutilsis the library thatpremaileruses to parse CSS. It will use the pythonloggingmodule
to mention all issues it has with parsing your CSS. If you want to capture
this, you have to pass incssutils_logging_handlerandcssutils_logging_level(optional). For example like this:>>>importlogging>>>importpremailer>>>fromioimportStringIO>>>mylog=StringIO()>>>myhandler=logging.StreamHandler(mylog)>>>p=premailer.Premailer(...cssutils_logging_handler=myhandler,...cssutils_logging_level=logging.INFO...)>>>result=p.transform("""
...         <html>
...         <style type="text/css">
...         @keyframes foo { from { opacity: 0; } to { opacity: 1; } }
...         </style>
...         <p>Hej</p>
...         </html>
... """)>>>mylog.getvalue()'CSSStylesheet: Unknown @rule found. [2:1: @keyframes]\n'If execution speed is on your mindIf execution speed is important, it’s very plausible that you’re not just converting
1 HTML document buta lotof HTML documents. Then, the first thing you should do
is avoid using thepremailer.transformfunction because it creates aPremailerclass instance every time.# WRONG WAY!frompremailerimporttransformforhtml_stringinget_html_documents():transformed=transform(html_string,base_url=MY_BASE_URL)# do something with 'transformed'Instead…# RIGHT WAYfrompremailerimportPremailerinstance=Premailer(base_url=MY_BASE_URL)forhtml_stringinget_html_documents():transformed=instance.transform(html_string)# do something with 'transformed'Another thing to watch out for when you’re reusing the same imported Python code
and reusing it is that internal memoize function caches might build up. The
environment variable to control isPREMAILER_CACHE_MAXSIZE. This parameter
requires a little bit of fine-tuning and calibration if your workload is really
big and memory even becomes an issue.Advanced optionsBelow are some advanced configuration options that probably doesn’t matter for
most people with regular load.Choosing the cache implementationBy default,premailerusesLFUCacheto cache
selectors, styles and parsed CSS strings. If LFU doesn’t serve your purpose, it
is possible to switch to an alternate implementation using below environment
variables.PREMAILER_CACHE: Can be LRU, LFU or TTL. Default is LFU.PREMAILER_CACHE_MAXSIZE: Maximum no. of items to be stored in cache. Defaults to 128.PREMAILER_CACHE_TTL: Time to live for cache entries. Only applicable for TTL cache. Defaults to 1 hour.Getting codingFirst clone the code and create whatever virtualenv you need, then run:pipinstall-e".[dev]"Then to run the tests, run:toxThis will run thewhole test suitefor every possible version of Python
it can find on your system. To run the tests more incrementally, open
up thetox.iniand see how it works.Code style is all blackAll code has to be formatted withBlackand the best tool for checking this istherapistsince it can help you run
all, help you fix things, and help you make sure linting is passing before
you git commit. This project also usesflake8to check other things
Black can’t check.To check linting withtoxuse:tox-elintTo install thetherapistpre-commit hook simply run:therapistinstallWhen you runtherapist runit will only check the files you’ve touched.
To run it for all files use:therapistrun--use-tracked-filesAnd to fix all/any issues run:therapistrun--use-tracked-files--fix