Skip to content

Commit

Permalink
Merge pull request #13 from iloveitaly/css_inline
Browse files Browse the repository at this point in the history
feat: support css_inline instead of toronado
  • Loading branch information
alex-oleshkevich authored Nov 30, 2024
2 parents 45dc5dd + 3e1dd09 commit 51f5012
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 17 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ mailer = Mailer(preprocessors=[attach_html_preprocessor])

### CSS inliner

> Requires `toronado` package installed
> Requires `css_inline` package installed
Out of the box we provide `mailers.preprocessors.css_inliner` utility that converts CSS classes into inline styles.

Expand Down
19 changes: 16 additions & 3 deletions mailers/preprocessors/cssliner.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import toronado
from email.message import EmailMessage, MIMEPart

import css_inline


def _process(part: MIMEPart) -> MIMEPart:
# https://github.com/Stranger6667/css-inline/tree/master/bindings/python
inliner = css_inline.CSSInliner()

content = part.get_content()
part.set_content(toronado.from_string(content), maintype="text", subtype="html")
part.set_content(
# set_content requires a byte literal, not a string
inliner.inline(content).encode("utf-8"),
maintype="text",
subtype="html",
)
return part


Expand All @@ -18,7 +27,11 @@ def css_inliner(message: EmailMessage) -> EmailMessage:
if not message["content-disposition"]:
_process(message)

if message.get_content_type() in ["multipart/alternative", "multipart/mixed", "multipart/related"]:
if message.get_content_type() in [
"multipart/alternative",
"multipart/mixed",
"multipart/related",
]:
for part in message.get_payload():
css_inliner(part)

Expand Down
16 changes: 6 additions & 10 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ aiosmtplib = { version = "^3.0", optional = true }
dkimpy = { version = "^1.0", optional = true }
anyio = ">=3.7.1,<5"
jinja2 = { version = "^3.0", optional = true }
css_inline = { version = ">=0.14", optional = true }

[tool.poetry.group.dev.dependencies]
pytest-asyncio = "*"
Expand All @@ -42,12 +43,15 @@ dkimpy = "^1"
jinja2 = "^3"
aiosmtplib = "*"
pytest = "^8.0"
toronado = "^0.1.0"

# required in case a parent directory overrides these options
[tool.pytest.ini_options]

[tool.poetry.extras]
jinja2 = ["jinja2"]
smtp = ["aiosmtplib"]
dkim = ["dkimpy"]
css_inline = ["css_inline"]

[tool.poetry.plugins.pytest11]
mailers = "mailers.pytest_plugin"
Expand Down Expand Up @@ -80,15 +84,7 @@ show_column_numbers = true
show_error_codes = true

[tool.ruff]
exclude = [
".egg",
".git",
".hg",
".mypy_cache",
".nox",
".tox",
".venv",
]
exclude = [".egg", ".git", ".hg", ".mypy_cache", ".nox", ".tox", ".venv"]
line-length = 120
indent-width = 4

Expand Down
7 changes: 4 additions & 3 deletions tests/preprocessors/test_cssinliner.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ def test_css_inliner_with_html_only_message() -> None:
message = EmailMessage()
message.set_content(html, subtype="html", charset="utf-8")
message = css_inliner(message)

assert message.get_content() == (
'<html><head></head><body><p class="text" style="color: red">hello</p>\n</body></html>'
'<html><head></head><body><p class="text" style="color: red;">hello</p>\n</body></html>'
)


Expand All @@ -31,7 +32,7 @@ def test_css_inliner_with_multipart_message() -> None:
message = css_inliner(message)
assert message.get_payload()[0].get_content() == '<style>.text {color: red; }</style><p class="text">hello</p>\n'
assert message.get_payload()[1].get_content() == (
'<html><head></head><body><p class="text" style="color: red">hello</p>\n</body></html>'
'<html><head></head><body><p class="text" style="color: red;">hello</p>\n</body></html>'
)


Expand All @@ -46,6 +47,6 @@ def test_css_inliner_with_multipart_with_attachments_message() -> None:
'<style>.text {color: red; }</style><p class="text">hello</p>\n'
)
assert message.get_payload()[0].get_payload()[1].get_content() == (
'<html><head></head><body><p class="text" style="color: red">hello</p>\n</body></html>'
'<html><head></head><body><p class="text" style="color: red;">hello</p>\n</body></html>'
)
assert base64.b64decode(message.get_payload(1).get_payload()) == html.encode()

0 comments on commit 51f5012

Please sign in to comment.