Metadata-Version: 2.4
Name: mjml-python
Version: 1.4.0
Classifier: Programming Language :: Rust
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: License :: OSI Approved :: MIT License
License-File: LICENSE
Summary: A Python wrapper for MRML (Rust port of MJML).
Requires-Python: >=3.8
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Bug Tracker, https://github.com/mgd020/mjml-python/issues
Project-URL: Homepage, https://github.com/mgd020/mjml-python

# `mjml-python`

Compile MJML at runtime **without** a Node.js service, external API, or subprocess.  
`mjml-python` is a Python wrapper around the Rust MJML engine [MRML](https://github.com/jdrouet/mrml), a high-performance port of the official [MJML](https://github.com/mjmlio/mjml).

## Why

Using MJML traditionally requires either:

- running the Node.js-based MJML CLI as a subprocess, or
- calling an external MJML API service.

Both approaches add memory overhead, latency, operational complexity, and cost.

From [MRML](https://github.com/jolimail/mrml#why):

> A Node.js server rendering an MJML template takes around 20 MB of RAM at startup and 130 MB under stress test. In Rust, less than 1.7 MB at startup and a bit less that 3 MB under stress test. The Rust version can also handle twice as many requests per second.

By embedding the Rust engine directly, `mjml-python` avoids all external dependencies while retaining full MJML compatibility.

## Installation

Install from [PyPI](https://pypi.org/project/mjml-python/):

```sh
pip install mjml-python
```

## Usage

Call `mjml2html()` with your MJML string:

```py
from mjml import mjml2html

html = mjml2html(
    '''
        <mjml>
          <mj-body>
            <mj-section>
              <mj-column>
                <mj-image width="100px" src="/assets/img/logo-small.png"></mj-image>
                <mj-divider border-color="#F45E43"></mj-divider>
                <!-- Say hello to the user -->
                <mj-text font-size="20px" color="#F45E43" font-family="Open Sans">Hello World</mj-text>
              </mj-column>
            </mj-section>
            <mj-section>
              <mj-column>
                <mj-social font-size="15px" icon-size="30px" mode="horizontal">
                  <mj-social-element name="facebook" href="https://mjml.io/">
                    Facebook
                  </mj-social-element>
                  <mj-social-element name="google" href="https://mjml.io/">
                    Google
                  </mj-social-element>
                  <mj-social-element  name="twitter" href="https://mjml.io/">
                    Twitter
                  </mj-social-element>
                </mj-social>
              </mj-column>
            </mj-section>
          </mj-body>
        </mjml>
    ''',
    disable_comments=True,
    social_icon_origin="https://example.com",
    fonts={
        "Open Sans": "https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,700",
        "Ubuntu": "https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700",
    }
)
```

**Example using Django templates**

```py
from django.core.mail import send_mail
from django.template.loader import render_to_string
from mjml import mjml2html

context = {'foo': 'bar'}
text_message = render_to_string('my_text_template.txt', context)
html_message = mjml2html(render_to_string('my_mjml_template.mjml', context))
send_mail(
    'Subject here',
    text_message,
    'from@example.com',
    ['to@example.com'],
    fail_silently=False,
    html_message=html_message,
)
```

## Configuration Options

`mjml-python` supports the following options:

| Name                 | Type                           | Default value | Comment                                                                          |
|----------------------|--------------------------------|---------------|----------------------------------------------------------------------------------|
| `disable_comments`   | `bool`                         | `False`       | Strip comments out of rendered HTML                                              |
| `social_icon_origin` | `str \| None`                  | `None`        | Custom URL origin for social icons. Icon name is appended (e.g. `facebook.png`). |
| `fonts`              | `dict[str, str] \| None`       | `None`        | Fonts imported in the HTML rendered by MJML.                                     |
| `include_loader`     | `Callable[[str], str] \| None` | `None`        | Fetch the included template using the path attribute.                            |

**Default fonts** (used when `fonts=None`):

```py
{
    "Open Sans": "https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,700",
    "Droid Sans": "https://fonts.googleapis.com/css?family=Droid+Sans:300,400,500,700",
    "Lato": "https://fonts.googleapis.com/css?family=Lato:300,400,500,700",
    "Roboto": "https://fonts.googleapis.com/css?family=Roboto:300,400,500,700",
    "Ubuntu": "https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700",
}       
```

## Why choose `mjml-python` instead of other MJML packages?

- **Simple, Pythonic API** – a single `mjml2html()` function that takes plain keyword arguments and returns a clean HTML string. No need to construct parser/render option objects or work with custom result classes.

- **Batteries-included defaults** – sensible defaults for fonts, comment handling, and include behavior, so most templates work with zero configuration.

- **Flexible include loader** – pass a Python callable to handle `<mj-include>` however you like (filesystem, database, HTTP, etc.) without learning MRML’s multiple loader types.

- **Minimal surface area** – intentionally focused on the common case: take MJML input → return production-ready HTML. No extra abstractions.

- **No external services or subprocesses required** – rendering is performed entirely inside the Python extension using the embedded Rust MJML engine. No Node.js installation, MJML CLI, or HTTP API needed.

- **Stable wheel builds** – lightweight ABI-3 wheels for all major platforms (CPython 3.7+), fast installation, and no compilation required on typical environments.

**In short:** `mjml-python` is ideal when you want a straightforward, Python-friendly way to render MJML without dealing with the full MRML configuration surface or external tooling.

## Development

With [Nix](https://nixos.org/): 

```sh
nix-shell
```

With Python 3.7+, Rust and Cargo installed:

```sh
python3 -m venv env
source env/bin/activate
pip install -r requirements.txt
maturin develop
python -m unittest
```

