Skip to main content

Send email via AWS SES from Python. Raw html/text, attachments, SES-stored templates, and suppression management. Your AWS account, no vendor lock-in.

Project description

wraps-email

Send email via AWS SES from Python — your AWS account, no vendor lock-in.

A thin, typed wrapper over SES: raw html/text, attachments, SES-stored templates, and suppression management. Built on httpx + botocore signing (no boto3), so it stays lightweight and a sync and async client can share one transport core.

pip install wraps-email      # or: uv add wraps-email
from wraps.email import WrapsEmail

email = WrapsEmail(region="us-east-1")
result = email.send(
    from_="you@yourdomain.com",
    to="user@example.com",
    subject="Hello from Python",
    html="<h1>It works</h1>",
    text="It works",
)
print(result.message_id)

Import as wraps.email; the distribution is wraps-email. from_ (not from, which is a Python keyword) is the sender.

Credentials

Resolved via the standard AWS chain — environment variables, shared config, SSO, OIDC/web-identity, assume-role, and IMDS. Override explicitly when you need to:

WrapsEmail()                                                    # default chain
WrapsEmail(profile="wraps-dogfood")                            # named profile
WrapsEmail(credentials={"access_key_id": "...", "secret_access_key": "..."})
WrapsEmail(role_arn="arn:aws:iam::123456789012:role/MyRole")   # assume-role / OIDC

Tip: send from a domain identity with DKIM (not a bare verified address) so mail authenticates at Gmail/Yahoo.

Sending

Attachments

Providing attachments switches the send to a raw MIME message automatically. content is bytes, or a string decoded per encoding ("utf-8" or "base64"); content_type is guessed from the filename when omitted. Bcc always rides the SES envelope, never the visible headers.

from wraps.email import Attachment

email.send(
    from_="you@yourdomain.com",
    to="user@example.com",
    bcc="audit@yourdomain.com",
    subject="Your report",
    html="<p>Attached.</p>",
    attachments=[Attachment(filename="report.csv", content="a,b\n1,2\n", content_type="text/csv")],
)

Batch

Send many independent messages concurrently. A failed message never aborts the batch; a malformed entry raises before anything is sent.

result = email.send_batch(
    [
        {"from_": "you@x.com", "to": "a@y.com", "subject": "Hi", "text": "1"},
        {"from_": "you@x.com", "to": "b@y.com", "subject": "Hi", "text": "2"},
    ],
    max_concurrency=10,
)
print(result.success_count, result.failure_count)
for entry in result.results:          # aligned to input order
    if not entry.success:
        print(entry.index, entry.error_code, entry.error)

Templates

Manage SES-stored templates and let SES render them at send time.

email.templates.create(name="welcome", subject="Hi {{name}}", html="<h1>{{name}}</h1>")
email.templates.get("welcome")
email.templates.list(page_size=20)          # .next_token to paginate
email.templates.update(name="welcome", subject="Hey {{name}}", html="<h1>{{name}}</h1>")
email.templates.delete("welcome")

email.send_template(
    template="welcome",
    from_="you@yourdomain.com",
    to="user@example.com",
    data={"name": "Sam"},
)

Suppression

The account-level SES suppression list (bounces and complaints).

email.suppression.add("bad@example.com", "COMPLAINT")
email.suppression.get("bad@example.com")     # -> SuppressionEntry | None
email.suppression.list(reason="BOUNCE")      # .next_token to paginate
email.suppression.remove("bad@example.com")

Errors

from wraps.email import SESError, ValidationError, CredentialsError

try:
    email.send(from_="you@x.com", to="user@y.com", subject="Hi", html="<p>Hi</p>")
except ValidationError as err:
    ...                 # bad input, caught before any AWS call (err.field)
except CredentialsError:
    ...                 # no AWS credentials resolved
except SESError as err:
    ...                 # err.code, err.request_id, err.retryable, err.status

Typed

Ships a PEP 561 py.typed marker; every public method has an explicit typed signature, so mypy / ty / Pyright check your calls and editors autocomplete them.

Status

0.1.0 — email SDK. Inbound (inbox), event history, reply threading, local template rendering, and an async client are planned. See the repo roadmap.

MIT licensed.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wraps_email-0.1.0.tar.gz (16.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wraps_email-0.1.0-py3-none-any.whl (18.9 kB view details)

Uploaded Python 3

File details

Details for the file wraps_email-0.1.0.tar.gz.

File metadata

  • Download URL: wraps_email-0.1.0.tar.gz
  • Upload date:
  • Size: 16.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for wraps_email-0.1.0.tar.gz
Algorithm Hash digest
SHA256 1d699909a48302a9adb5f750a8c27e5a8cabe446483cc66981faeceeb0d6788d
MD5 ff347b6b2985e68fa4def34691d74bd6
BLAKE2b-256 fee2845a2fb152599457ec2c8aba16fa3534b6d3e98cd31ef295962a041b907a

See more details on using hashes here.

File details

Details for the file wraps_email-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: wraps_email-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 18.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for wraps_email-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 34ff9fd2f4aa27fb0e231a9089a9659933e905fa56bc537043811c6e8b6071ca
MD5 e630dfc757ae0e128d7693356f96a9d8
BLAKE2b-256 5039b0758a2f419e73d6e70535e64d4b5a10db76629371a506f78beb07995584

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page