python-emails

repository·master·Indexed 19 days ago

https://github.com/lavr/python-emails

A high-level Python library for building, transforming, and sending complex emails with HTML, attachments, and inline images. It simplifies email construction by removing the need to manually build MIME trees and provides features like CSS inlining, image embedding, and asynchronous sending via aiosmtplib. Includes dedicated integrations for Django (DjangoMessage) and Flask (flask-emails), as well as loaders for creating messages from URLs, ZIP archives, directories, and RFC 822 files.

Tokens
15.6K
Snippets
55
Records
65
Agent score
64%

What's inside python-emails

  1. Core features of python-emails

    master

    The python-emails library provides a high-level API for:

    • Message Composition: Create HTML and plain-text messages in a single object, including support for headers, CC/BCC, and Reply-To.
    • Attachments & Media: Easily add file attachments and embed inline images.
    • Template Rendering: Support for Jinja2, Mako, and string templates in html, text, and subject fields.
    • HTML Processing: Apply transformations via message.transform(), including CSS inlining and HTML cleanup.
    • Security: DKIM signing support.
    • Loading: Load messages from URLs, HTML files, directories, ZIP archives, and RFC 822 messages.
    • Delivery: SMTP sending with SSL/TLS support, including async sending via send_async() (requires emails[async]).
    • Integrations: DjangoMessage for Django and flask-emails for Flask.
  2. Compare python-emails with alternative email libraries

    master

    When choosing an email library for your Python project, consider these alternatives based on your specific requirements:

    • smtplib + email (Standard Library): Best for low-level SMTP transport and full control. Requires manual MIME assembly for complex HTML/attachments. python-emails is built on top of these to provide a higher-level API.
    • yagmail: Best for quick setup and Gmail/SMTP clients with auto-detected content types and OAuth2 support.
    • red-mail: Best for data-driven reports requiring built-in Jinja2 templates and prettified HTML tables.
    • envelope: Best when email encryption (GPG/S/MIME) or a CLI interface is a requirement.

    Choose python-emails if your primary focus is HTML email. It treats HTML as a first-class citizen by providing:

    • Loading HTML from URLs, files, ZIP archives, or directories.
    • Automatic CSS inlining and image embedding via transformations.
    • Support for multiple template engines (Jinja2, Mako, string templates).
    • Built-in DKIM signing and Django integration.
  3. Configure SSL vs STARTTLS encryption

    master

    The library supports two encryption modes. Note that you cannot set both ssl and tls to True simultaneously; doing so will raise a ValueError.

    • Implicit SSL (ssl=True): Connects over TLS from the start. Typically used with port 465.
    • STARTTLS (tls=True): Connects in plain text, then upgrades to TLS. Typically used with port 587.
    # Implicit SSL (Port 465)
    message.send(smtp={"host": "mail.example.com", "port": 465, "ssl": True, "user": "me", "password": "secret"})
    
    # STARTTLS (Port 587)
    message.send(smtp={"host": "smtp.example.com", "port": 587, "tls": True, "user": "me", "password": "secret"})
  4. Use Templates for dynamic email content

    master

    You can pass template instances to the html, text, or subject parameters of a Message to enable dynamic content.

    To use Jinja templates, install the dependency: pip install "emails[jinja]".

    Supported Template Classes:

    • JinjaTemplate(template_text, environment=None): Uses Jinja2 syntax.
    • StringTemplate(template_text, safe_substitute=True): Uses Python's string.Template syntax ($variable or ${variable}).
    • MakoTemplate(template_text, **kwargs): Uses Mako syntax (requires mako package).
    from emails.template import JinjaTemplate
    
    msg = emails.Message(
        html=JinjaTemplate("<p>Hello {{ name }}!</p>"),
        subject=JinjaTemplate("Welcome, {{ name }}"),
        mail_from="noreply@example.com"
    )
    # Rendering variables during send:
    msg.send(render={"name": "Alice"}, smtp={"host": "localhost"})
  5. Quickstart: Create and send an email

    master

    To get started with python-emails, you can use the emails.html() utility to create a message and the .send() method to dispatch it via SMTP.

    Creating a simple email

    Use emails.html() to define the content. You can pass parameters like subject, mail_to, and mail_from directly.

    Sending via SMTP

    Pass an smtp dictionary to the send() method. This dictionary typically includes host, port, and authentication details. Always check the status_code of the returned SMTPResponse to verify delivery.

    Attachments and Inline Images

    • Use .attach() to add files. You can specify content_disposition to control how the attachment is handled.
    • For inline images, use the cid: protocol in your HTML to reference attached images.

    Using Templates

    Use JinjaTemplate and pass a render={} dictionary to the message to inject dynamic data into your HTML content.

    Generating without sending

    If you only want to see the resulting email without sending it, use .as_string() or .as_message().

  6. Quickstart: Build and send an HTML email

    master

    You can create and send HTML emails using the emails.html constructor. This allows you to define a subject, HTML content, and the sender's identity (mail_from). Once the message object is created, use the .send() method to dispatch it via SMTP. The .send() method accepts a to recipient and an smtp configuration dictionary containing host, port, and tls settings.

    import emails
    
    message = emails.html(
        subject="Hi from python-emails!",
        html="<html><p>Hello, <strong>World!</strong></p></html>",
        mail_from=("Alice", "alice@example.com"),
    )
    response = message.send(
        to="bob@example.com",
        smtp={"host": "smtp.example.com", "port": 587, "tls": True},
    )
    assert response.status_code == 250
  7. Embed inline images in HTML

    master

    To display images directly within the HTML body instead of as attachments, use the cid: (Content-ID) URI scheme.

    1. In your HTML, reference the image using <img src="cid:filename.ext">.
    2. Attach the image using message.attach() with content_disposition="inline".
    message = emails.html(
        html='<p>Hello! <img src="cid:logo.png"></p>',
        subject="With inline image",
        mail_from="sender@example.com"
    )
    message.attach(
        filename="logo.png",
        data=open("logo.png", "rb"),
        content_disposition="inline"
    )
  8. Debug email sending with SMTP tracing and Python logging

    master

    There are three primary ways to debug email issues in python-emails:

    1. SMTP Protocol Tracing: Set debug=1 in the smtp dictionary passed to message.send(). This prints the full SMTP conversation (commands and responses) to stdout. Useful for diagnosing TLS or authentication failures.
    2. Python Logging: The library uses the standard logging module. You can enable debug logging for specific internal loggers:
      • emails.backend.smtp.backend: Connection management and retries.
      • emails.backend.smtp.client: SMTP client operations.
    3. Inspecting the Message: Use message.as_string() to view the raw RFC 822 output, including the MIME structure and headers, before sending.
    # 1. SMTP Tracing
    message.send(
        to="user@example.com",
        smtp={"host": "smtp.example.com", "port": 587, "tls": True, "user": "me", "password": "secret", "debug": 1}
    )
    
    # 2. Python Logging
    import logging
    logging.getLogger("emails.backend.smtp.client").setLevel(logging.DEBUG)
    
    # 3. Inspecting raw output
    print(message.as_string())
  9. Filter images during HTML transformation

    master

    You can pass a callable to the load_images parameter of message.transform() to control which images are processed. This is useful for skipping tracking pixels or specific domains.

    Alternatively, you can use the data-emails attribute directly in your HTML:

    • data-emails="ignore": skip loading this image
    • data-emails="inline": load as an inline attachment
    def should_load(element, hints=None, **kwargs):
        # Skip tracking pixels
        src = element.attrib.get("src", "")
        if "track" in src or "pixel" in src:
            return False
        return True
    
    message.transform(load_images=should_load)
  10. Configure SMTP connections

    master

    You can send emails by passing an smtp dictionary to message.send(). The library manages the connection internally. For more granular control or explicit connection management, use the SMTPBackend class, which supports the context manager pattern to ensure connections are closed automatically.

    # Using a dictionary for simple management
    response = message.send(
        to="user@example.com",
        smtp={"host": "smtp.example.com", "port": 587, "tls": True, "user": "me", "password": "secret"}
    )
    
    # Using SMTPBackend for explicit control and connection reuse
    from emails.backend.smtp import SMTPBackend
    
    with SMTPBackend(host="smtp.example.com", port=587, tls=True, user="me", password="secret") as backend:
        for recipient in recipients:
            message.send(to=recipient, smtp=backend)
  11. Advanced Usage: HTML transformations and SMTP

    master

    For more complex email requirements, python-emails provides advanced transformation and connection controls.

    HTML Transformations

    The .transform() method allows for sophisticated HTML manipulation. Key parameters include:

    • css_inline: Inlines CSS styles into the HTML.
    • remove_unsafe_tags: Strips potentially dangerous HTML tags.
    • set_content_type_meta: Sets metadata for content types.
    • load_images: Loads external images.
    • images_inline: Converts external images to inline attachments.

    Note: make_links_absolute and update_stylesheet are deprecated as the underlying engine (premailer) handles these differently.

    To customize how links or images are handled, use the transformer's specialized methods:

    • transformer.apply_to_images()
    • transformer.apply_to_links()

    SMTP Connections

    When sending many emails, you can reuse a backend connection to improve performance. You can also configure timeouts and choose between SSL and TLS for secure connections.