Advanced Features¶
Dynamic Variable Interpolation¶
ExMailer supports f-string style placeholders in your email body or template.
emailer.send_email(
subject="System Alert",
body="Node {node_id} reported status: {status}",
recipients=["admin@company.com"],
template_vars={"node_id": "SRV-01", "status": "CRITICAL"}
)
Warning: Escaping literal braces
If your email body contains literal
{or}characters that are not meant to be template variable placeholders — for example, inline CSS rules or JavaScript snippets — escape them by doubling the brace:{{and}}. If unescaped braces are present whiletemplate_varsis passed, ExMailer skips body substitution entirely and logs a warning (the email is still sent, but your placeholders remain literal).
Custom Template Engine¶
You can define high-fidelity HTML templates with CSS and register them for reuse.
- Define the Template — Use
{body}as the placeholder for the email content. Because templates are processed throughstr.format(), all CSS block braces must be escaped as{{and}}. - Register — Use
register_custom_templateto make the template available by name. - Execute — Reference the template by name in
send_email.
from exmailer import ExchangeEmailer, register_custom_template
CORPORATE_TEMPLATE = """<!DOCTYPE html>
<html>
<head>
<style>
body {{ font-family: Arial, sans-serif; background-color: #f4f4f4; margin: 0; padding: 0; }}
.container {{ max-width: 600px; margin: 40px auto; background: #ffffff; border-radius: 8px; padding: 32px; }}
.footer {{ margin-top: 24px; font-size: 12px; color: #888; }}
</style>
</head>
<body>
<div class="container">
{body}
<div class="footer">This message was sent automatically. Please do not reply.</div>
</div>
</body>
</html>"""
# Register once at startup (module-level function, not a method)
register_custom_template("corporate", CORPORATE_TEMPLATE)
emailer = ExchangeEmailer(verbose=False)
# Use by name in any send call
emailer.send_email(
subject="Quarterly Report",
body="<p>Please find the quarterly summary attached.</p>",
recipients=["finance@company.com"],
template="corporate",
)
Configurable Debug Logging¶
When verbose=True, ExMailer writes its own debug-level log messages to a log file
(scoped to the exmailer logger — low-level exchangelib wire traffic, which can
contain credentials, is deliberately not captured). By default that file is named
exchange_debug.log in the current working directory.
Use the log_file parameter to point it elsewhere:
The parent directory must already exist. log_file has no effect when verbose=False.
Targeting a Specific Exchange Version¶
ExMailer communicates with Exchange using a specific server build version that is sent
in every EWS request. By default this is [15, 1, 2248, 0] (Exchange 2016). If your
server rejects requests or behaves unexpectedly, override it via the exchange_build
key in your JSON or YAML configuration:
Both a four-element integer list [major, minor, build, revision] and the
equivalent dotted string are accepted; invalid values fall back to the default
with a logged warning. (Exchange 2016 reports 15.1.x, Exchange 2019 reports
15.2.x.)