Production-ready Python IMAP client with transport layer, mailbox services, OAuth2, and connection pooling.
Project description
Python Sage IMAP
Production-oriented IMAP for Python 3.10+ — UID-first mailbox operations, CONDSTORE sync, IDLE, OAuth2, SPECIAL-USE folders, an optional async API (sage_imap.aio), and an optional IMAP ORM (sage_imap.orm).
Current release:
2.0.0— sync API on stdlibimaplib; async via optional[async]extra (aioimaplib).
Why this library?
Python Sage IMAP targets applications that need reliable email access beyond thin imaplib wrappers:
- UID-first search, fetch, move, and flag operations via
IMAPMailboxUIDServiceandIMAPSession - Streaming fetch with
iter_uid_fetch/iter_messagesandParseMode(headers-only, minimal, full, raw) - Incremental sync with CONDSTORE (
MailboxSyncState,CHANGEDSINCE) - IDLE for push-style mailbox notifications
- OAuth2 with refresh (
OAuth2Config,ensure_access_token) - Resilience — retries, connection pooling (
use_pool=True), metrics, health checks - Async parity —
AsyncIMAPSessionundersage_imap.aio(install[async]) - IMAP ORM (optional) — manager/queryset layer, Pydantic schemas, multi-tenant
account_id, CONDSTORE checkpoints (pip install python-sage-imap[orm])
Installation
pip install python-sage-imap
Async support:
pip install python-sage-imap[async]
IMAP ORM (manager/queryset API, Pydantic schemas):
pip install python-sage-imap[orm]
Async ORM:
pip install python-sage-imap[orm,async]
Requirements: Python 3.10+, network access to an IMAP server (TLS recommended).
Development
git clone https://github.com/sageteamorg/python-sage-imap.git
cd python-sage-imap
poetry install
poetry install -E async # optional, for sage_imap.aio tests
poetry run pytest -m "not integration"
Integration tests against a Mailcow-compatible stack:
make integration-up
make integration-test
make integration-down
Set IMAP_HOST, IMAP_USER, IMAP_PASSWORD, and optionally IMAP_PORT. See docker/mailcow/README.md.
Quick start (sync)
Use IMAPSession as the primary entry point:
from sage_imap import IMAPSession, IMAPSearchCriteria, SpecialUse
with IMAPSession("imap.example.com", "user@example.com", "app-password") as session:
session.select("INBOX")
result = session.search(IMAPSearchCriteria.UNSEEN)
trash = session.special_folder(SpecialUse.TRASH)
for msg in session.iter_messages(result.to_uid_message_set(), batch_size=50):
print(msg.uid, msg.subject)
More: docs/SESSION.md · Read the Docs
Quick start (async)
import asyncio
from sage_imap.aio import AsyncIMAPSession
from sage_imap.helpers.search import IMAPSearchCriteria
async def main():
async with AsyncIMAPSession("imap.example.com", "user@example.com", "secret") as session:
await session.select("INBOX")
result = await session.search(IMAPSearchCriteria.UNSEEN)
async for msg in session.iter_messages(result.to_uid_message_set()):
print(msg.subject)
asyncio.run(main())
See docs/ASYNC.md and examples/09_async_session.py.
Quick start (IMAP ORM)
Optional Django-style managers over live IMAP (no SQL). Requires [orm]:
import os
from sage_imap.orm import ImapAccountConfig, ImapMessage, ImapORM, LoadLevel
from sage_imap.orm.schemas import ImapMessageSummarySchema
config = ImapAccountConfig(
account_id="demo",
host=os.environ["IMAP_HOST"],
username=os.environ["IMAP_USER"],
password=os.environ["IMAP_PASSWORD"],
)
with ImapORM.open("demo", config=config) as orm:
orm.select_mailbox("INBOX")
qs = ImapMessage.objects.filter(unread=True).limit(10).with_load_level(LoadLevel.HEADERS)
for msg in qs.iter():
print(ImapMessageSummarySchema.from_imap_message(msg).model_dump(mode="json"))
Tutorial: IMAP ORM tutorial · Examples: examples/10_orm_sync.py, examples/11_orm_async.py
Sync vs async
| Topic | Sync | Async |
|---|---|---|
| Import | from sage_imap import IMAPSession |
from sage_imap.aio import AsyncIMAPSession |
| Transport | imaplib + threading.RLock |
aioimaplib + asyncio.Lock |
| Install | pip install python-sage-imap |
pip install python-sage-imap[async] |
| OAuth refresh | stdlib (urllib) |
httpx (or thread fallback) |
| ORM | Sync | Async |
|---|---|---|
| Import | from sage_imap.orm import ImapORM, ImapMessage |
from sage_imap.orm.async_session import AsyncImapORM |
| Install | pip install python-sage-imap[orm] |
pip install python-sage-imap[orm,async] |
Async is not re-exported from top-level sage_imap (by design). See docs/MIGRATION_v2.md when upgrading from 1.x.
Lower-level API
Services remain available for fine-grained control:
from sage_imap import IMAPClient, IMAPMailboxUIDService, IMAPSearchCriteria
with IMAPClient("imap.example.com", "user@example.com", "secret") as client:
caps = client.transport.get_capabilities()
mailbox = IMAPMailboxUIDService(client)
mailbox.select("INBOX")
result = mailbox.uid_search(IMAPSearchCriteria.ALL)
for msg in mailbox.iter_uid_fetch(result.to_uid_message_set()):
print(msg.subject)
IMAPClient delegates raw imaplib methods (e.g. list, select) when connected; prefer UID services for message operations.
Examples
Runnable scripts live under examples/. Configure credentials via environment variables:
export IMAP_HOST=imap.example.com
export IMAP_USER=user@example.com
export IMAP_PASSWORD=secret
poetry run python examples/01_basic_client_usage.py
| Script | Topic |
|---|---|
01_basic_client_usage.py |
Client, pooling, metrics |
02_connection_pooling_example.py |
use_pool=True |
03_retry_and_resilience_example.py |
Retries and recovery |
04_monitoring_and_metrics_example.py |
ConnectionMetrics |
05_advanced_client_features.py |
OAuth, TLS, health |
06_mailbox_operations_example.py |
Mailbox CRUD |
07_advanced_mailbox_features.py |
Upload, bulk ops |
08_mailbox_uid_operations.py |
UID search/fetch |
09_async_session.py |
Async session |
10_orm_sync.py |
Sync IMAP ORM |
11_orm_async.py |
Async IMAP ORM |
See examples/README.md.
Configuration
from sage_imap import ConnectionConfig, IMAPSession, build_ssl_context
config = ConnectionConfig(
host="imap.example.com",
username="user@example.com",
password="secret",
port=993,
use_ssl=True,
timeout=30.0,
max_retries=5,
retry_delay=2.0,
enable_monitoring=True,
enable_background_health=False,
)
with IMAPSession.from_config(config) as session:
session.select("INBOX")
Documentation
- Online: https://python-sage-imap.readthedocs.io/
- IMAP ORM tutorial: https://python-sage-imap.readthedocs.io/en/latest/tutorials/orm/index.html
- Session facade: docs/SESSION.md
- Async: docs/ASYNC.md
- v2 migration: docs/MIGRATION_v2.md
- Changelog: CHANGELOG.md
Testing
poetry run pytest -m "not integration"
poetry run pytest tests/aio -m "not integration" # requires -E async
poetry run pytest --cov=sage_imap --cov-report=html
Contributing
See CONTRIBUTING.md and SECURITY.md.
make setup-dev
make test
make lint
License
MIT — see LICENSE.
Support
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file python_sage_imap-2.1.0b1.tar.gz.
File metadata
- Download URL: python_sage_imap-2.1.0b1.tar.gz
- Upload date:
- Size: 105.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.0.1 CPython/3.12.4 Windows/11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b6d9b2be16c2d0b9d41b61d53936dffca14bac260344ba9db36170112d17eb97
|
|
| MD5 |
1e317b20e128d7490dae5fa1f9fe2d3e
|
|
| BLAKE2b-256 |
1833d293e4d53512784af38bc2f9b855e744cf0d11f79feff30784cf05a4cfcf
|
File details
Details for the file python_sage_imap-2.1.0b1-py3-none-any.whl.
File metadata
- Download URL: python_sage_imap-2.1.0b1-py3-none-any.whl
- Upload date:
- Size: 132.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.0.1 CPython/3.12.4 Windows/11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e773e1704adaa6e3e5460da69335a54d6bbd470715871d257768f5453a973ce6
|
|
| MD5 |
b5113037d8e40d8bb8937f4efd3b5378
|
|
| BLAKE2b-256 |
180e6d41ef820873b552e6b790819c3ae577594fcdadc3266af1445876cdf065
|