README.md

August 1, 2026 · View on GitHub

pipen-email logo

Email notifications for pipen pipelines on status changes.
Uses Python stdlib `smtplib` + `email.message` — zero additional dependencies beyond `pipen`.

Installation

pip install pipen-email

Quickstart

from pipen import Pipen, Proc

class SayHello(Proc):
    input = "name:var"
    output = "outfile:file:greeting.txt"
    script = 'echo "Hello, {{in.name}}!" > {{out.outfile}}'

pipeline = Pipen(
    name="my-pipeline",
    loglevel="debug",
    plugin_opts={
        # SMTP — email_host is required for any notifications to fire
        "email_host": "smtp.example.com",
        "email_port": 587,
        "email_use_tls": True,
        "email_username": "user",
        "email_password": "pass",
        # Envelope
        "email_to": "you@example.com",
        # Job digest (opt-in)
        "email_on_job": True,
        "email_batch_interval": 60,
    },
)
pipeline.set_start(SayHello).set_data(["Alice", "Bob"]).run()

Note: email_host must be set to a truthy value. Without it, no emails will be sent — all notification keys are silently ignored. This acts as a master switch so you can disable the plugin entirely by omitting email_host.

SMTP Examples

Local debug server

# Terminal 1 — start a debug SMTP server
python -m aiosmtpd -n -l localhost:1025

# Terminal 2 — run the pipeline
python my_pipeline.py
plugin_opts = {
    "email_host": "localhost",
    "email_port": 1025,
    "email_to": "dev@localhost",
}

Gmail (with App Password)

Generate an app password at myaccount.google.com/apppasswords.

plugin_opts = {
    "email_host": "smtp.gmail.com",
    "email_port": 587,
    "email_use_tls": True,
    "email_username": "you@gmail.com",
    "email_password": "your-16-char-app-password",
    "email_to": "you@gmail.com",
}

No-auth relay

plugin_opts = {
    "email_host": "smtp-relay.internal",
    "email_port": 25,
    "email_to": "team@example.com",
}

AWS SES

plugin_opts = {
    "email_host": "email-smtp.us-east-1.amazonaws.com",
    "email_port": 587,
    "email_use_tls": True,
    "email_username": "AKIA...",
    "email_password": "your-ses-smtp-password",
    "email_to": "team@example.com",
}

Configuration Reference

All options live in pipen.config.plugin_opts. Set them via Pipen(plugin_opts={...}), .pipen.toml, or per-process Proc.plugin_opts.

SMTP connection

KeyDefaultDescription
email_hostNoneSMTP server hostname. Must be set for any emails to send.
email_port25SMTP server port
email_use_tlsFalseUse STARTTLS on connect
email_use_sslFalseUse SMTPS (SSL on port 465)
email_usernameNoneSMTP auth username
email_passwordNoneSMTP auth password
email_timeout30SMTP connection timeout in seconds

Email envelope

KeyDefaultDescription
email_from"pipen@localhost"From address
email_toNoneTo address(es) — comma-separated string or list
email_ccNoneCC address(es) — comma-separated string or list
email_bccNoneBCC address(es) — comma-separated string or list
email_subject_prefix"[pipen]"Prefix prepended to every subject line

Pipeline lifecycle

KeyDefaultDescription
email_on_startTrueSend email when pipeline starts
email_on_completeTrueSend email when pipeline finishes (success or failure)

Process lifecycle

KeyDefaultDescription
email_on_proc_startTrueSend email when a process starts
email_on_proc_doneTrueSend email when a process completes, fails, or returns cached
email_on_proc_shutdownTrueSend email when a process receives a signal (SIGTERM, SIGKILL, etc.)

Note: email_on_proc_done covers all terminal statuses — completed, failed, and cached. There is no separate email_on_proc_failed toggle. When a process fails, the email body includes the failed job's script path, stdout/stderr file paths, and full stderr content.

Job lifecycle

KeyDefaultDescription
email_on_jobFalseEnable batched job status digest emails
email_batch_interval60Minimum seconds between job digest emails

Logging

KeyDefaultDescription
email_loglevel"info"Log level for email send events. One of "debug", "info", "warning", "error", or "critical". Case-insensitive.

How It Works

Email guard

All hooks check email_host first via the internal _should_send method. If email_host is not set (or falsy), every notification silently skips. This means you can conditionally enable the plugin:

# Disable in dev, enable in CI
plugin_opts = {
    "email_host": "smtp.example.com" if os.environ.get("CI") else None,
    "email_to": "team@example.com",
}

Pipeline emails

Sent on on_start and on_complete. Body format:

Pipeline: my-pipeline
Status: COMPLETED
Workdir: .pipen/my-pipeline
Outdir: /output/path
Processes: 3
Process names: ProcA, ProcB, ProcC

Process emails

Sent on on_proc_start, on_proc_done, and on_proc_shutdown. Body format:

Pipeline: my-pipeline
Process: ProcA
Status: COMPLETED
Jobs: 10
Workdir: .pipen/my-pipeline/ProcA

Job status summary:
INIT: 0-9
SUCCEEDED: 0-9

On process failure, the body also includes details about the first failed job:

Information for job #3:
- script: .pipen/my-pipeline/ProcA/3/job.script
- stdout: .pipen/my-pipeline/ProcA/3/job.stdout
- stderr: .pipen/my-pipeline/ProcA/3/job.stderr

Full STDERR:
----------------
Traceback (most recent call last):
...

Job digest emails

When email_on_job is True, each job lifecycle event records its index. A digest email is sent when email_batch_interval seconds have elapsed since the last digest. Digest body format:

Job status digest:
---------------------
INIT: 0-9
RUNNING: 0-9
SUCCEEDED: 0-2
FAILED: 3-5

Job digest emails are also flushed automatically in on_proc_done (via the interval gate).

Shutdown email

Only sent when the process receives an actual signal (sig is truthy). For normal process completion, on_proc_shutdown is called with sig=None and no email is sent — the on_proc_done email covers that case.

Error Handling

SMTP errors are caught and logged at ERROR level. The pipeline never fails due to an email error — _send_email returns False on failure, and the caller continues.

See Also